Merge "libbinder_ndk: private SharedRefBase construction" am: 1a4c7aee26 am: 0235b3260b am: db5aceffb3

Change-Id: Ic7aff0f8071ec0080c1bd1efb7b7006212b9163b
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e048a19..0473bb8 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                cmds/idlcli/
                include/input/
+               libs/binder/fuzzer/
                libs/binder/ndk/
                libs/binderthreadstate/
                libs/graphicsenv/
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
similarity index 62%
copy from cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
copy to aidl/gui/android/view/LayerMetadataKey.aidl
index 7f74ceb..7026ca8 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,13 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.view;
 
-/**
-  * Token used by the IDumpstateListener to watch for dumpstate death.
-  * {@hide}
-  */
-interface IDumpstateToken {
+/** @hide */
+@Backing(type="int")
+enum LayerMetadataKey {
+    METADATA_OWNER_UID = 1,
+    METADATA_WINDOW_TYPE = 2,
+    METADATA_TASK_ID = 3,
+    METADATA_MOUSE_CURSOR = 4,
 }
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 09aee89..acca11a 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -66,7 +66,6 @@
     name: "dumpstate_aidl",
     srcs: [
         "binder/android/os/IDumpstateListener.aidl",
-        "binder/android/os/IDumpstateToken.aidl",
         "binder/android/os/IDumpstate.aidl",
     ],
     path: "binder",
@@ -77,6 +76,7 @@
     defaults: ["dumpstate_cflag_defaults"],
     shared_libs: [
         "android.hardware.dumpstate@1.0",
+        "android.hardware.dumpstate@1.1",
         "libziparchive",
         "libbase",
         "libbinder",
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index 10d3d17..87ea520 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -58,8 +58,6 @@
     exit(0);
 }
 
-class DumpstateToken : public BnDumpstateToken {};
-
 }  // namespace
 
 DumpstateService::DumpstateService() : ds_(nullptr) {
@@ -81,38 +79,6 @@
     return android::OK;
 }
 
-// Note: this method is part of the old flow and is not expected to be used in combination
-// with startBugreport.
-binder::Status DumpstateService::setListener(const std::string& name,
-                                             const sp<IDumpstateListener>& listener,
-                                             bool getSectionDetails,
-                                             sp<IDumpstateToken>* returned_token) {
-    *returned_token = nullptr;
-    if (name.empty()) {
-        MYLOGE("setListener(): name not set\n");
-        return binder::Status::ok();
-    }
-    if (listener == nullptr) {
-        MYLOGE("setListener(): listener not set\n");
-        return binder::Status::ok();
-    }
-    std::lock_guard<std::mutex> lock(lock_);
-    if (ds_ == nullptr) {
-        ds_ = &(Dumpstate::GetInstance());
-    }
-    if (ds_->listener_ != nullptr) {
-        MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_->listener_name_.c_str());
-        return binder::Status::ok();
-    }
-
-    ds_->listener_name_ = name;
-    ds_->listener_ = listener;
-    ds_->report_section_ = getSectionDetails;
-    *returned_token = new DumpstateToken();
-
-    return binder::Status::ok();
-}
-
 binder::Status DumpstateService::startBugreport(int32_t calling_uid,
                                                 const std::string& calling_package,
                                                 android::base::unique_fd bugreport_fd,
@@ -121,8 +87,7 @@
                                                 const sp<IDumpstateListener>& listener) {
     MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
 
-    // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a
-    // time.
+    // Ensure there is only one bugreport in progress at a time.
     std::lock_guard<std::mutex> lock(lock_);
     if (ds_ != nullptr) {
         MYLOGE("Error! There is already a bugreport in progress. Returning.");
@@ -204,19 +169,17 @@
     dprintf(fd, "progress:\n");
     ds_->progress_->Dump(fd, "  ");
     dprintf(fd, "args: %s\n", ds_->options_->args.c_str());
-    dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str());
+    dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode.c_str());
     dprintf(fd, "version: %s\n", ds_->version_.c_str());
     dprintf(fd, "bugreport_dir: %s\n", destination.c_str());
     dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str());
     dprintf(fd, "log_path: %s\n", ds_->log_path_.c_str());
     dprintf(fd, "tmp_path: %s\n", ds_->tmp_path_.c_str());
     dprintf(fd, "path: %s\n", ds_->path_.c_str());
-    dprintf(fd, "extra_options: %s\n", ds_->options_->extra_options.c_str());
     dprintf(fd, "base_name: %s\n", ds_->base_name_.c_str());
     dprintf(fd, "name: %s\n", ds_->name_.c_str());
     dprintf(fd, "now: %ld\n", ds_->now_);
     dprintf(fd, "is_zipping: %s\n", ds_->IsZipping() ? "true" : "false");
-    dprintf(fd, "listener: %s\n", ds_->listener_name_.c_str());
     dprintf(fd, "notification title: %s\n", ds_->options_->notification_title.c_str());
     dprintf(fd, "notification description: %s\n", ds_->options_->notification_description.c_str());
 
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index aaaa428..6dc0225 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -24,7 +24,6 @@
 #include <binder/BinderService.h>
 
 #include "android/os/BnDumpstate.h"
-#include "android/os/BnDumpstateToken.h"
 #include "dumpstate.h"
 
 namespace android {
@@ -38,9 +37,6 @@
     static char const* getServiceName();
 
     status_t dump(int fd, const Vector<String16>& args) override;
-    binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
-                               bool getSectionDetails,
-                               sp<IDumpstateToken>* returned_token) override;
 
     binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
                                   android::base::unique_fd bugreport_fd,
diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md
index c818c05..26dabbb 100644
--- a/cmds/dumpstate/README.md
+++ b/cmds/dumpstate/README.md
@@ -92,6 +92,12 @@
 adb shell setprop dumpstate.version default
 ```
 
+## To set Bugreport API workflow for bugreport
+
+```
+adb shell setprop settings_call_bugreport_api true
+```
+
 ## Code style and formatting
 
 Use the style defined at the
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index cb2d8b8..3f359c8 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -17,24 +17,12 @@
 package android.os;
 
 import android.os.IDumpstateListener;
-import android.os.IDumpstateToken;
 
 /**
   * Binder interface for the currently running dumpstate process.
   * {@hide}
   */
 interface IDumpstate {
-    // TODO: remove method once startBugReport is used by Shell.
-    /*
-     * Sets the listener for this dumpstate progress.
-     *
-     * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already
-     * set (the listener behaves like a Highlander: There Can be Only One).
-     * Set {@code getSectionDetails} to true in order to receive callbacks with per section
-     * progress details
-     */
-    IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener,
-                                boolean getSectionDetails);
 
     // NOTE: If you add to or change these modes, please also change the corresponding enums
     // in system server, in BugreportParams.java.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 310b5ce..3a79357 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -64,6 +64,8 @@
 #include <android-base/unique_fd.h>
 #include <android/content/pm/IPackageManagerNative.h>
 #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
+#include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
+#include <android/hardware/dumpstate/1.1/types.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <android/os/IIncidentCompanion.h>
 #include <binder/IServiceManager.h>
@@ -85,7 +87,11 @@
 #include "DumpstateService.h"
 #include "dumpstate.h"
 
-using ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using IDumpstateDevice_1_0 = ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
+using IDumpstateDevice_1_1 = ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
+using ::android::hardware::dumpstate::V1_1::DumpstateMode;
+using ::android::hardware::dumpstate::V1_1::DumpstateStatus;
+using ::android::hardware::dumpstate::V1_1::toString;
 using ::std::literals::chrono_literals::operator""ms;
 using ::std::literals::chrono_literals::operator""s;
 
@@ -240,14 +246,6 @@
     return true;
 }
 
-static bool IsFileEmpty(const std::string& file_path) {
-    std::ifstream file(file_path, std::ios::binary | std::ios::ate);
-    if(file.bad()) {
-        MYLOGE("Cannot open file: %s\n", file_path.c_str());
-        return true;
-    }
-    return file.tellg() <= 0;
-}
 
 int64_t GetModuleMetadataVersion() {
     auto binder = defaultServiceManager()->getService(android::String16("package_native"));
@@ -297,11 +295,8 @@
 };
 static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles);
 
-static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
 static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
 static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
-static constexpr char PROPERTY_EXTRA_TITLE[] = "dumpstate.options.title";
-static constexpr char PROPERTY_EXTRA_DESCRIPTION[] = "dumpstate.options.description";
 
 static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
 
@@ -705,8 +700,8 @@
     RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"},
                    CommandOptions::WithTimeout(1).Always().Build());
     printf("Bugreport format version: %s\n", version_.c_str());
-    printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
-           PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->extra_options.c_str());
+    printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s bugreport_mode=%s\n", id_, pid_,
+           PropertiesHelper::IsDryRun(), options_->args.c_str(), options_->bugreport_mode.c_str());
     printf("\n");
 }
 
@@ -956,6 +951,31 @@
     unlink(path.c_str());
 }
 
+static void DumpVisibleWindowViews() {
+    if (!ds.IsZipping()) {
+        MYLOGD("Not dumping visible views because it's not a zipped bugreport\n");
+        return;
+    }
+    DurationReporter duration_reporter("VISIBLE WINDOW VIEWS");
+    const std::string path = ds.bugreport_internal_dir_ + "/tmp_visible_window_views";
+    auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
+                O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+                S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+    if (fd < 0) {
+        MYLOGE("Could not open %s to dump visible views.\n", path.c_str());
+        return;
+    }
+    RunCommandToFd(fd, "", {"cmd", "window", "dump-visible-window-views"},
+                   CommandOptions::WithTimeout(120).Build());
+    bool empty = 0 == lseek(fd, 0, SEEK_END);
+    if (!empty) {
+        ds.AddZipEntry("visible_windows.zip", path);
+    } else {
+        MYLOGW("Failed to dump visible windows\n");
+    }
+    unlink(path.c_str());
+}
+
 static void DumpIpTablesAsRoot() {
     RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
     RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
@@ -1340,6 +1360,8 @@
 
     RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20);
 
+    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpVisibleWindowViews);
+
     DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
     DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
     DumpFile("SLAB INFO", "/proc/slabinfo");
@@ -1547,11 +1569,7 @@
  * Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport
  * with the caller.
  */
-static Dumpstate::RunStatus DumpstateDefault() {
-    // Invoking the following dumpsys calls before DumpTraces() to try and
-    // keep the system stats as close to its initial state as possible.
-    RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical);
-
+Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() {
     // Capture first logcat early on; useful to take a snapshot before dumpstate logs take over the
     // buffer.
     DoLogcat();
@@ -1880,8 +1898,8 @@
             std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i])));
     }
 
-    sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService());
-    if (dumpstate_device == nullptr) {
+    sp<IDumpstateDevice_1_0> dumpstate_device_1_0(IDumpstateDevice_1_0::getService());
+    if (dumpstate_device_1_0 == nullptr) {
         MYLOGE("No IDumpstateDevice implementation\n");
         return;
     }
@@ -1912,29 +1930,54 @@
         handle.get()->data[i] = fd.release();
     }
 
-    // Given that bugreport is required to diagnose failures, it's better to
-    // set an arbitrary amount of timeout for IDumpstateDevice than to block the
-    // rest of bugreport. In the timeout case, we will kill dumpstate board HAL
-    // and grab whatever dumped
-    std::packaged_task<bool()>
-            dumpstate_task([paths, dumpstate_device, &handle]() -> bool {
-            android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle.get());
+    // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount
+    // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we
+    // will kill the HAL and grab whatever it dumped in time.
+    constexpr size_t timeout_sec = 30;
+    // Prefer version 1.1 if available. New devices launching with R are no longer allowed to
+    // implement just 1.0.
+    const char* descriptor_to_kill;
+    using DumpstateBoardTask = std::packaged_task<bool()>;
+    DumpstateBoardTask dumpstate_board_task;
+    sp<IDumpstateDevice_1_1> dumpstate_device_1_1(
+        IDumpstateDevice_1_1::castFrom(dumpstate_device_1_0));
+    if (dumpstate_device_1_1 != nullptr) {
+        MYLOGI("Using IDumpstateDevice v1.1");
+        descriptor_to_kill = IDumpstateDevice_1_1::descriptor;
+        dumpstate_board_task = DumpstateBoardTask([this, dumpstate_device_1_1, &handle]() -> bool {
+            ::android::hardware::Return<DumpstateStatus> status =
+                dumpstate_device_1_1->dumpstateBoard_1_1(handle.get(), options_->dumpstate_hal_mode,
+                                                         SEC_TO_MSEC(timeout_sec));
+            if (!status.isOk()) {
+                MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+                return false;
+            } else if (status != DumpstateStatus::OK) {
+                MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", toString(status).c_str());
+                return false;
+            }
+            return true;
+        });
+    } else {
+        MYLOGI("Using IDumpstateDevice v1.0");
+        descriptor_to_kill = IDumpstateDevice_1_0::descriptor;
+        dumpstate_board_task = DumpstateBoardTask([dumpstate_device_1_0, &handle]() -> bool {
+            ::android::hardware::Return<void> status =
+                dumpstate_device_1_0->dumpstateBoard(handle.get());
             if (!status.isOk()) {
                 MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
                 return false;
             }
             return true;
         });
+    }
+    auto result = dumpstate_board_task.get_future();
+    std::thread(std::move(dumpstate_board_task)).detach();
 
-    auto result = dumpstate_task.get_future();
-    std::thread(std::move(dumpstate_task)).detach();
-
-    constexpr size_t timeout_sec = 30;
     if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
         MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec);
-        if (!android::base::SetProperty("ctl.interface_restart",
-                                        android::base::StringPrintf("%s/default",
-                                                                    IDumpstateDevice::descriptor))) {
+        if (!android::base::SetProperty(
+                "ctl.interface_restart",
+                android::base::StringPrintf("%s/default", descriptor_to_kill))) {
             MYLOGE("Couldn't restart dumpstate HAL\n");
         }
     }
@@ -1966,15 +2009,14 @@
             continue;
         }
         AddZipEntry(kDumpstateBoardFiles[i], paths[i]);
+        printf("*** See %s entry ***\n", kDumpstateBoardFiles[i].c_str());
     }
-
-    printf("*** See dumpstate-board.txt entry ***\n");
 }
 
 static void ShowUsage() {
     fprintf(stderr,
             "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] "
-            "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
+            "[-z] [-s] [-S] [-q] [-P] [-R] [-V version]\n"
             "  -h: display this help message\n"
             "  -b: play sound file instead of vibrate, at beginning of job\n"
             "  -e: play sound file instead of vibrate, at end of job\n"
@@ -1984,11 +2026,8 @@
             "  -s: write output to control socket (for init)\n"
             "  -S: write file location to control socket (for init; requires -z)\n"
             "  -q: disable vibrate\n"
-            "  -B: send broadcast when finished\n"
-            "  -P: send broadcast when started and update system properties on "
-            "progress (requires -B)\n"
-            "  -R: take bugreport in remote mode (requires -z, -d and -B, "
-            "shouldn't be used with -P)\n"
+            "  -P: send broadcast when started and do progress updates\n"
+            "  -R: take bugreport in remote mode (requires -z and -d, shouldn't be used with -P)\n"
             "  -w: start binder service and make it wait for a call to startBugreport\n"
             "  -v: prints the dumpstate header and exit\n");
 }
@@ -2045,41 +2084,6 @@
     return true;
 }
 
-static std::string SHA256_file_hash(const std::string& filepath) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
-            | O_CLOEXEC | O_NOFOLLOW)));
-    if (fd == -1) {
-        MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno));
-        return nullptr;
-    }
-
-    SHA256_CTX ctx;
-    SHA256_Init(&ctx);
-
-    std::vector<uint8_t> buffer(65536);
-    while (1) {
-        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd.get(), buffer.data(), buffer.size()));
-        if (bytes_read == 0) {
-            break;
-        } else if (bytes_read == -1) {
-            MYLOGE("read(%s): %s\n", filepath.c_str(), strerror(errno));
-            return nullptr;
-        }
-
-        SHA256_Update(&ctx, buffer.data(), bytes_read);
-    }
-
-    uint8_t hash[SHA256_DIGEST_LENGTH];
-    SHA256_Final(hash, &ctx);
-
-    char hash_buffer[SHA256_DIGEST_LENGTH * 2 + 1];
-    for(size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) {
-        sprintf(hash_buffer + (i * 2), "%02x", hash[i]);
-    }
-    hash_buffer[sizeof(hash_buffer) - 1] = 0;
-    return std::string(hash_buffer);
-}
-
 static void SendBroadcast(const std::string& action, const std::vector<std::string>& args) {
     // clang-format off
     std::vector<std::string> am = {"/system/bin/cmd", "activity", "broadcast", "--user", "0",
@@ -2139,26 +2143,26 @@
     }
 
     if (ds.options_->do_fb) {
-        ds.screenshot_path_ = ds.GetPath(".png");
+        ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
     }
     ds.tmp_path_ = ds.GetPath(".tmp");
     ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
 
-    std::string destination = ds.options_->bugreport_fd.get() != -1
+    std::string destination = ds.CalledByApi()
                                   ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get())
                                   : ds.bugreport_internal_dir_.c_str();
     MYLOGD(
-        "Bugreport dir: %s\n"
-        "Base name: %s\n"
-        "Suffix: %s\n"
-        "Log path: %s\n"
-        "Temporary path: %s\n"
-        "Screenshot path: %s\n",
+        "Bugreport dir: [%s] "
+        "Base name: [%s] "
+        "Suffix: [%s] "
+        "Log path: [%s] "
+        "Temporary path: [%s] "
+        "Screenshot path: [%s]\n",
         destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
         ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
 
     if (ds.options_->do_zip_file) {
-        ds.path_ = ds.GetPath(".zip");
+        ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
         MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
         create_parent_dirs(ds.path_.c_str());
         ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
@@ -2172,37 +2176,10 @@
 }
 
 /*
- * Finalizes writing to the file by renaming or zipping the tmp file to the final location,
+ * Finalizes writing to the file by zipping the tmp file to the final location,
  * printing zipped file status, etc.
  */
 static void FinalizeFile() {
-    /* check if user changed the suffix using system properties */
-    std::string name =
-        android::base::GetProperty(android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
-    bool change_suffix = false;
-    if (!name.empty()) {
-        /* must whitelist which characters are allowed, otherwise it could cross directories */
-        std::regex valid_regex("^[-_a-zA-Z0-9]+$");
-        if (std::regex_match(name.c_str(), valid_regex)) {
-            change_suffix = true;
-        } else {
-            MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
-        }
-    }
-    if (change_suffix) {
-        MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
-        ds.name_ = name;
-        if (!ds.screenshot_path_.empty()) {
-            std::string new_screenshot_path = ds.GetPath(".png");
-            if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
-                MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
-                       new_screenshot_path.c_str(), strerror(errno));
-            } else {
-                ds.screenshot_path_ = new_screenshot_path;
-            }
-        }
-    }
-
     bool do_text_file = true;
     if (ds.options_->do_zip_file) {
         if (!ds.FinishZipFile()) {
@@ -2210,25 +2187,6 @@
             do_text_file = true;
         } else {
             do_text_file = false;
-            // If the user has changed the suffix, we need to change the zip file name.
-            std::string new_path = ds.GetPath(".zip");
-            if (ds.path_ != new_path) {
-                MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
-                if (rename(ds.path_.c_str(), new_path.c_str())) {
-                    MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
-                           strerror(errno));
-                } else {
-                    ds.path_ = new_path;
-                }
-            }
-        }
-    }
-    if (do_text_file) {
-        ds.path_ = ds.GetPath(".txt");
-        MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(), ds.tmp_path_.c_str());
-        if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
-            MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(), strerror(errno));
-            ds.path_.clear();
         }
     }
     if (ds.options_->use_control_socket) {
@@ -2243,49 +2201,6 @@
     }
 }
 
-/* Broadcasts that we are done with the bugreport */
-static void SendBugreportFinishedBroadcast() {
-    // TODO(b/111441001): use callback instead of broadcast.
-    if (!ds.path_.empty()) {
-        MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
-        // clang-format off
-
-        std::vector<std::string> am_args = {
-             "--receiver-permission", "android.permission.DUMP",
-             "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
-             "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
-             "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
-             "--es", "android.intent.extra.BUGREPORT", ds.path_,
-             "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
-        };
-        // clang-format on
-        if (ds.options_->do_fb && !android::os::IsFileEmpty(ds.screenshot_path_)) {
-            am_args.push_back("--es");
-            am_args.push_back("android.intent.extra.SCREENSHOT");
-            am_args.push_back(ds.screenshot_path_);
-        }
-        if (!ds.options_->notification_title.empty()) {
-            am_args.push_back("--es");
-            am_args.push_back("android.intent.extra.TITLE");
-            am_args.push_back(ds.options_->notification_title);
-            if (!ds.options_->notification_description.empty()) {
-                am_args.push_back("--es");
-                am_args.push_back("android.intent.extra.DESCRIPTION");
-                am_args.push_back(ds.options_->notification_description);
-            }
-        }
-        if (ds.options_->is_remote_mode) {
-            am_args.push_back("--es");
-            am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
-            am_args.push_back(SHA256_file_hash(ds.path_));
-            SendBroadcast("com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
-        } else {
-            SendBroadcast("com.android.internal.intent.action.BUGREPORT_FINISHED", am_args);
-        }
-    } else {
-        MYLOGE("Skipping finished broadcast because bugreport could not be generated\n");
-    }
-}
 
 static inline const char* ModeToString(Dumpstate::BugreportMode mode) {
     switch (mode) {
@@ -2307,119 +2222,61 @@
 }
 
 static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options) {
-    options->extra_options = ModeToString(mode);
+    options->bugreport_mode = ModeToString(mode);
     switch (mode) {
         case Dumpstate::BugreportMode::BUGREPORT_FULL:
-            options->do_broadcast = true;
             options->do_fb = true;
+            options->dumpstate_hal_mode = DumpstateMode::FULL;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
             // Currently, the dumpstate binder is only used by Shell to update progress.
             options->do_start_service = true;
             options->do_progress_updates = true;
             options->do_fb = false;
-            options->do_broadcast = true;
+            options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
             options->do_vibrate = false;
             options->is_remote_mode = true;
             options->do_fb = false;
-            options->do_broadcast = true;
+            options->dumpstate_hal_mode = DumpstateMode::REMOTE;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WEAR:
             options->do_start_service = true;
             options->do_progress_updates = true;
             options->do_zip_file = true;
             options->do_fb = true;
-            options->do_broadcast = true;
+            options->dumpstate_hal_mode = DumpstateMode::WEAR;
             break;
+        // TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY.
         case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
             options->telephony_only = true;
             options->do_progress_updates = true;
             options->do_fb = false;
-            options->do_broadcast = true;
+            options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_WIFI:
             options->wifi_only = true;
             options->do_zip_file = true;
             options->do_fb = false;
-            options->do_broadcast = true;
+            options->dumpstate_hal_mode = DumpstateMode::WIFI;
             break;
         case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
             break;
     }
 }
 
-static Dumpstate::BugreportMode getBugreportModeFromProperty() {
-    // If the system property is not set, it's assumed to be a default bugreport.
-    Dumpstate::BugreportMode mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT;
-
-    std::string extra_options = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
-    if (!extra_options.empty()) {
-        // Framework uses a system property to override some command-line args.
-        // Currently, it contains the type of the requested bugreport.
-        if (extra_options == "bugreportplus") {
-            mode = Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE;
-        } else if (extra_options == "bugreportfull") {
-            mode = Dumpstate::BugreportMode::BUGREPORT_FULL;
-        } else if (extra_options == "bugreportremote") {
-            mode = Dumpstate::BugreportMode::BUGREPORT_REMOTE;
-        } else if (extra_options == "bugreportwear") {
-            mode = Dumpstate::BugreportMode::BUGREPORT_WEAR;
-        } else if (extra_options == "bugreporttelephony") {
-            mode = Dumpstate::BugreportMode::BUGREPORT_TELEPHONY;
-        } else if (extra_options == "bugreportwifi") {
-            mode = Dumpstate::BugreportMode::BUGREPORT_WIFI;
-        } else {
-            MYLOGE("Unknown extra option: %s\n", extra_options.c_str());
-        }
-        // Reset the property
-        android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
-    }
-    return mode;
-}
-
-// TODO: Move away from system properties when we have options passed via binder calls.
-/* Sets runtime options from the system properties and then clears those properties. */
-static void SetOptionsFromProperties(Dumpstate::DumpOptions* options) {
-    Dumpstate::BugreportMode mode = getBugreportModeFromProperty();
-    SetOptionsFromMode(mode, options);
-
-    options->notification_title = android::base::GetProperty(PROPERTY_EXTRA_TITLE, "");
-    if (!options->notification_title.empty()) {
-        // Reset the property
-        android::base::SetProperty(PROPERTY_EXTRA_TITLE, "");
-
-        options->notification_description =
-            android::base::GetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
-        if (!options->notification_description.empty()) {
-            // Reset the property
-            android::base::SetProperty(PROPERTY_EXTRA_DESCRIPTION, "");
-        }
-        MYLOGD("notification (title:  %s, description: %s)\n", options->notification_title.c_str(),
-               options->notification_description.c_str());
-    }
-}
-
 static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
-    MYLOGI("do_zip_file: %d\n", options.do_zip_file);
-    MYLOGI("do_add_date: %d\n", options.do_add_date);
-    MYLOGI("do_vibrate: %d\n", options.do_vibrate);
-    MYLOGI("use_socket: %d\n", options.use_socket);
-    MYLOGI("use_control_socket: %d\n", options.use_control_socket);
-    MYLOGI("do_fb: %d\n", options.do_fb);
-    MYLOGI("do_broadcast: %d\n", options.do_broadcast);
-    MYLOGI("is_remote_mode: %d\n", options.is_remote_mode);
-    MYLOGI("show_header_only: %d\n", options.show_header_only);
-    MYLOGI("do_start_service: %d\n", options.do_start_service);
-    MYLOGI("telephony_only: %d\n", options.telephony_only);
-    MYLOGI("wifi_only: %d\n", options.wifi_only);
-    MYLOGI("do_progress_updates: %d\n", options.do_progress_updates);
-    MYLOGI("fd: %d\n", options.bugreport_fd.get());
-    MYLOGI("extra_options: %s\n", options.extra_options.c_str());
-    MYLOGI("args: %s\n", options.args.c_str());
-    MYLOGI("notification_title: %s\n", options.notification_title.c_str());
-    MYLOGI("notification_description: %s\n", options.notification_description.c_str());
+    MYLOGI(
+        "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_fb: %d "
+        "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+        "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
+        "args: %s\n",
+        options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
+        options.do_fb, options.is_remote_mode, options.show_header_only, options.do_start_service,
+        options.telephony_only, options.wifi_only, options.do_progress_updates,
+        options.bugreport_fd.get(), options.bugreport_mode.c_str(),
+        toString(options.dumpstate_hal_mode).c_str(), options.args.c_str());
 }
 
 void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2434,7 +2291,6 @@
     bugreport_fd.reset(dup(bugreport_fd_in.get()));
     screenshot_fd.reset(dup(screenshot_fd_in.get()));
 
-    extra_options = ModeToString(bugreport_mode);
     SetOptionsFromMode(bugreport_mode, this);
 }
 
@@ -2446,9 +2302,6 @@
             // clang-format off
             case 'd': do_add_date = true;            break;
             case 'z': do_zip_file = true;            break;
-            // o=use_outfile not supported anymore.
-            // TODO(b/111441001): Remove when all callers have migrated.
-            case 'o': break;
             case 's': use_socket = true;             break;
             case 'S': use_control_socket = true;     break;
             case 'v': show_header_only = true;       break;
@@ -2456,7 +2309,6 @@
             case 'p': do_fb = true;                  break;
             case 'P': do_progress_updates = true;    break;
             case 'R': is_remote_mode = true;         break;
-            case 'B': do_broadcast = true;           break;
             case 'V':                                break;  // compatibility no-op
             case 'w':
                 // This was already processed
@@ -2482,7 +2334,6 @@
     // Reset next index used by getopt so this can be called multiple times, for eg, in tests.
     optind = 1;
 
-    SetOptionsFromProperties(this);
     return status;
 }
 
@@ -2491,7 +2342,7 @@
         return false;
     }
 
-    if ((do_zip_file || do_add_date || do_progress_updates || do_broadcast) && !OutputToFile()) {
+    if ((do_zip_file || do_add_date || do_progress_updates) && !OutputToFile()) {
         return false;
     }
 
@@ -2499,11 +2350,7 @@
         return false;
     }
 
-    if (do_progress_updates && !do_broadcast) {
-        return false;
-    }
-
-    if (is_remote_mode && (do_progress_updates || !do_broadcast || !do_zip_file || !do_add_date)) {
+    if (is_remote_mode && (do_progress_updates || !do_zip_file || !do_add_date)) {
         return false;
     }
     return true;
@@ -2599,11 +2446,8 @@
         return RunStatus::OK;
     }
 
-    if (options_->bugreport_fd.get() != -1) {
-        // If the output needs to be copied over to the caller's fd, get user consent.
-        android::String16 package(calling_package.c_str());
-        CheckUserConsent(calling_uid, package);
-    }
+    MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
+            calling_uid, calling_package.c_str());
 
     // Redirect output if needed
     bool is_redirecting = options_->OutputToFile();
@@ -2620,8 +2464,6 @@
     id_ = ++last_id;
     android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
 
-    MYLOGI("begin\n");
-
     if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
         MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno));
     } else {
@@ -2644,10 +2486,8 @@
         MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
     }
 
-    MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", id_, options_->args.c_str(),
-           options_->extra_options.c_str());
-
-    MYLOGI("bugreport format version: %s\n", version_.c_str());
+    MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
+           id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str());
 
     do_early_screenshot_ = options_->do_progress_updates;
 
@@ -2672,18 +2512,13 @@
         PrepareToWriteToFile();
 
         if (options_->do_progress_updates) {
-            if (options_->do_broadcast) {
-                // clang-format off
-                std::vector<std::string> am_args = {
-                     "--receiver-permission", "android.permission.DUMP",
-                     "--es", "android.intent.extra.NAME", name_,
-                     "--ei", "android.intent.extra.ID", std::to_string(id_),
-                     "--ei", "android.intent.extra.PID", std::to_string(pid_),
-                     "--ei", "android.intent.extra.MAX", std::to_string(progress_->GetMax()),
-                };
-                // clang-format on
-                SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
-            }
+            // clang-format off
+            std::vector<std::string> am_args = {
+                 "--receiver-permission", "android.permission.DUMP",
+            };
+            // clang-format on
+            // Send STARTED broadcast for apps that listen to bugreport generation events
+            SendBroadcast("com.android.internal.intent.action.BUGREPORT_STARTED", am_args);
             if (options_->use_control_socket) {
                 dprintf(control_socket_fd_, "BEGIN:%s\n", path_.c_str());
             }
@@ -2751,13 +2586,23 @@
     PrintHeader();
 
     if (options_->telephony_only) {
+        MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateTelephonyOnly();
         DumpstateBoard();
     } else if (options_->wifi_only) {
+        MaybeCheckUserConsent(calling_uid, calling_package);
         DumpstateWifiOnly();
     } else {
+        // Invoking the critical dumpsys calls before DumpTraces() to try and
+        // keep the system stats as close to its initial state as possible.
+        RunDumpsysCritical();
+
+        // Run consent check only after critical dumpsys has finished -- so the consent
+        // isn't going to pollute the system state / logs.
+        MaybeCheckUserConsent(calling_uid, calling_package);
+
         // Dump state for the default case. This also drops root.
-        RunStatus s = DumpstateDefault();
+        RunStatus s = DumpstateDefaultAfterCritical();
         if (s != RunStatus::OK) {
             if (s == RunStatus::USER_CONSENT_DENIED) {
                 HandleUserConsentDenied();
@@ -2771,15 +2616,15 @@
         TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout)));
     }
 
-    // Rename, and/or zip the (now complete) .tmp file within the internal directory.
+    // Zip the (now complete) .tmp file within the internal directory.
     if (options_->OutputToFile()) {
         FinalizeFile();
     }
 
-    // Share the final file with the caller if the user has consented.
+    // Share the final file with the caller if the user has consented or Shell is the caller.
     Dumpstate::RunStatus status = Dumpstate::RunStatus::OK;
-    if (options_->bugreport_fd.get() != -1) {
-        status = CopyBugreportIfUserConsented();
+    if (CalledByApi()) {
+        status = CopyBugreportIfUserConsented(calling_uid);
         if (status != Dumpstate::RunStatus::OK &&
             status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
             // Do an early return if there were errors. We make an exception for consent
@@ -2819,12 +2664,6 @@
         }
     }
 
-    /* tell activity manager we're done */
-    if (options_->do_broadcast) {
-        SendBugreportFinishedBroadcast();
-        // Note that listener_ is notified in Run();
-    }
-
     MYLOGD("Final progress: %d/%d (estimated %d)\n", progress_->Get(), progress_->GetMax(),
            progress_->GetInitialMax());
     progress_->Save();
@@ -2848,14 +2687,20 @@
                : RunStatus::OK;
 }
 
-void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
+void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
+    if (calling_uid == AID_SHELL || !CalledByApi()) {
+        // No need to get consent for shell triggered dumpstates, or not through
+        // bugreporting API (i.e. no fd to copy back).
+        return;
+    }
     consent_callback_ = new ConsentCallback();
     const String16 incidentcompanion("incidentcompanion");
     sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+    android::String16 package(calling_package.c_str());
     if (ics != nullptr) {
         MYLOGD("Checking user consent via incidentcompanion service\n");
         android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
-            calling_uid, calling_package, String16(), String16(),
+            calling_uid, package, String16(), String16(),
             0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
     } else {
         MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
@@ -2867,6 +2712,10 @@
            ds.consent_callback_->getResult() == UserConsentResult::DENIED;
 }
 
+bool Dumpstate::CalledByApi() const {
+    return ds.options_->bugreport_fd.get() != -1 ? true : false;
+}
+
 void Dumpstate::CleanupFiles() {
     android::os::UnlinkAndLogOnError(tmp_path_);
     android::os::UnlinkAndLogOnError(screenshot_path_);
@@ -2879,10 +2728,15 @@
     return USER_CONSENT_DENIED;
 }
 
-Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() {
+Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented(int32_t calling_uid) {
     // If the caller has asked to copy the bugreport over to their directory, we need explicit
-    // user consent.
-    UserConsentResult consent_result = consent_callback_->getResult();
+    // user consent (unless the caller is Shell).
+    UserConsentResult consent_result;
+    if (calling_uid == AID_SHELL) {
+        consent_result = UserConsentResult::APPROVED;
+    } else {
+        consent_result = consent_callback_->getResult();
+    }
     if (consent_result == UserConsentResult::UNAVAILABLE) {
         // User has not responded yet.
         uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
@@ -3746,15 +3600,13 @@
     }
 
     if (listener_ != nullptr) {
-        if (percent % 5 == 0) {
-            // We don't want to spam logcat, so only log multiples of 5.
-            MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max,
-                   percent);
+        if (percent % 10 == 0) {
+            // We don't want to spam logcat, so only log multiples of 10.
+            MYLOGD("Setting progress: %d/%d (%d%%)\n", progress, max, percent);
         } else {
             // stderr is ignored on normal invocations, but useful when calling
             // /system/bin/dumpstate directly for debuggging.
-            fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(),
-                    progress, max, percent);
+            fprintf(stderr, "Setting progress: %d/%d (%d%%)\n", progress, max, percent);
         }
 
         listener_->onProgress(percent);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 82bf821..111c098 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -27,6 +27,7 @@
 
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.1/types.h>
 #include <android/os/BnIncidentAuthListener.h>
 #include <android/os/IDumpstate.h>
 #include <android/os/IDumpstateListener.h>
@@ -344,6 +345,11 @@
     bool IsUserConsentDenied() const;
 
     /*
+     * Returns true if dumpstate is called by bugreporting API
+     */
+    bool CalledByApi() const;
+
+    /*
      * Structure to hold options that determine the behavior of dumpstate.
      */
     struct DumpOptions {
@@ -354,7 +360,6 @@
         bool use_socket = false;
         bool use_control_socket = false;
         bool do_fb = false;
-        bool do_broadcast = false;
         bool is_remote_mode = false;
         bool show_header_only = false;
         bool do_start_service = false;
@@ -362,20 +367,24 @@
         bool wifi_only = false;
         // Whether progress updates should be published.
         bool do_progress_updates = false;
+        // The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
+        // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead.
+        // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
+        ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
+            ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
         // File descriptor to output zip file.
         android::base::unique_fd bugreport_fd;
         // File descriptor to screenshot file.
         android::base::unique_fd screenshot_fd;
-        // TODO: rename to MODE.
-        // Extra options passed as system property.
-        std::string extra_options;
+        // Bugreport mode of the bugreport.
+        std::string bugreport_mode;
         // Command-line arguments as string
         std::string args;
         // Notification title and description
         std::string notification_title;
         std::string notification_description;
 
-        /* Initializes options from commandline arguments and system properties. */
+        /* Initializes options from commandline arguments. */
         RunStatus Initialize(int argc, char* argv[]);
 
         /* Initializes options from the requested mode. */
@@ -451,8 +460,6 @@
 
     // Binder object listening to progress.
     android::sp<android::os::IDumpstateListener> listener_;
-    std::string listener_name_;
-    bool report_section_;
 
     // List of open tombstone dump files.
     std::vector<DumpData> tombstone_data_;
@@ -485,7 +492,9 @@
   private:
     RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
 
-    void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+    RunStatus DumpstateDefaultAfterCritical();
+
+    void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
 
     // Removes the in progress files output files (tmp file, zip/txt file, screenshot),
     // but leaves the log file alone.
@@ -493,8 +502,9 @@
 
     RunStatus HandleUserConsentDenied();
 
-    // Copies bugreport artifacts over to the caller's directories provided there is user consent.
-    RunStatus CopyBugreportIfUserConsented();
+    // Copies bugreport artifacts over to the caller's directories provided there is user consent or
+    // called by Shell.
+    RunStatus CopyBugreportIfUserConsented(int32_t calling_uid);
 
     // Used by GetInstance() only.
     explicit Dumpstate(const std::string& version = VERSION_CURRENT);
diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc
index 14937b8..e491a4b 100644
--- a/cmds/dumpstate/dumpstate.rc
+++ b/cmds/dumpstate/dumpstate.rc
@@ -11,8 +11,7 @@
 
 # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once
 # it is finished.
-service dumpstatez /system/bin/dumpstate -S -d -z \
-        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
+service dumpstatez /system/bin/dumpstate -S -d -z
     socket dumpstate stream 0660 shell log
     class main
     disabled
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 256dc05..dac90d9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -195,21 +195,16 @@
     static Dumpstate& ds;
     static std::chrono::milliseconds duration;
     static void SetUpTestCase() {
-        property_set("dumpstate.options", "bugreportplus");
         // clang-format off
         char* argv[] = {
             (char*)"dumpstate",
             (char*)"-d",
             (char*)"-z",
-            (char*)"-B",
-            (char*)"-o",
-            (char*)dirname(android::base::GetExecutablePath().c_str())
+            (char*)"-B"
         };
         // clang-format on
         sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
         ds.listener_ = listener;
-        ds.listener_name_ = "Smokey";
-        ds.report_section_ = true;
         auto start = std::chrono::steady_clock::now();
         ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv);
         auto end = std::chrono::steady_clock::now();
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 99d482f..76b9960 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -36,19 +36,22 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/dumpstate/1.1/types.h>
 #include <cutils/properties.h>
 
 namespace android {
 namespace os {
 namespace dumpstate {
 
+using ::android::hardware::dumpstate::V1_1::DumpstateMode;
 using ::testing::EndsWith;
 using ::testing::HasSubstr;
-using ::testing::IsNull;
 using ::testing::IsEmpty;
+using ::testing::IsNull;
 using ::testing::NotNull;
-using ::testing::StrEq;
 using ::testing::StartsWith;
+using ::testing::StrEq;
 using ::testing::Test;
 using ::testing::internal::CaptureStderr;
 using ::testing::internal::CaptureStdout;
@@ -148,10 +151,9 @@
         options_ = Dumpstate::DumpOptions();
     }
     void TearDown() {
-        // Reset the property
-        property_set("dumpstate.options", "");
     }
     Dumpstate::DumpOptions options_;
+    android::base::unique_fd fd;
 };
 
 TEST_F(DumpOptionsTest, InitializeNone) {
@@ -174,7 +176,7 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.do_broadcast);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeAdbBugreport) {
@@ -200,8 +202,8 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.do_broadcast);
     EXPECT_FALSE(options_.use_socket);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) {
@@ -226,28 +228,15 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.do_broadcast);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeFullBugReport) {
-    // clang-format off
-    char* argv[] = {
-        const_cast<char*>("bugreport"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-p"),
-        const_cast<char*>("-B"),
-        const_cast<char*>("-z"),
-    };
-    // clang-format on
-    property_set("dumpstate.options", "bugreportfull");
-
-    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
-
-    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd);
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
-    EXPECT_TRUE(options_.do_broadcast);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -260,27 +249,13 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
-    // clang-format off
-    char* argv[] = {
-        const_cast<char*>("bugreport"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-p"),
-        const_cast<char*>("-B"),
-        const_cast<char*>("-z"),
-    };
-    // clang-format on
-
-    property_set("dumpstate.options", "bugreportplus");
-
-    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
-
-    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd);
     EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_broadcast);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
     EXPECT_FALSE(options_.do_fb);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -291,27 +266,13 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
-    // clang-format off
-    char* argv[] = {
-        const_cast<char*>("bugreport"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-p"),
-        const_cast<char*>("-B"),
-        const_cast<char*>("-z"),
-    };
-    // clang-format on
-
-    property_set("dumpstate.options", "bugreportremote");
-
-    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
-
-    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd);
     EXPECT_TRUE(options_.do_add_date);
-    EXPECT_TRUE(options_.do_broadcast);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.is_remote_mode);
     EXPECT_FALSE(options_.do_vibrate);
     EXPECT_FALSE(options_.do_fb);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
 
     // Other options retain default values
     EXPECT_FALSE(options_.use_control_socket);
@@ -321,27 +282,13 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeWearBugReport) {
-    // clang-format off
-    char* argv[] = {
-        const_cast<char*>("bugreport"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-p"),
-        const_cast<char*>("-B"),
-        const_cast<char*>("-z"),
-    };
-    // clang-format on
-
-    property_set("dumpstate.options", "bugreportwear");
-
-    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
-
-    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd);
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_fb);
-    EXPECT_TRUE(options_.do_broadcast);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.do_start_service);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -352,27 +299,13 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
-    // clang-format off
-    char* argv[] = {
-        const_cast<char*>("bugreport"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-p"),
-        const_cast<char*>("-B"),
-        const_cast<char*>("-z"),
-    };
-    // clang-format on
-
-    property_set("dumpstate.options", "bugreporttelephony");
-
-    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
-
-    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd);
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_FALSE(options_.do_fb);
-    EXPECT_TRUE(options_.do_broadcast);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.telephony_only);
     EXPECT_TRUE(options_.do_progress_updates);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -383,26 +316,12 @@
 }
 
 TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
-    // clang-format off
-    char* argv[] = {
-        const_cast<char*>("bugreport"),
-        const_cast<char*>("-d"),
-        const_cast<char*>("-p"),
-        const_cast<char*>("-B"),
-        const_cast<char*>("-z"),
-    };
-    // clang-format on
-
-    property_set("dumpstate.options", "bugreportwifi");
-
-    Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
-
-    EXPECT_EQ(status, Dumpstate::RunStatus::OK);
+    options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd);
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_FALSE(options_.do_fb);
-    EXPECT_TRUE(options_.do_broadcast);
     EXPECT_TRUE(options_.do_zip_file);
     EXPECT_TRUE(options_.wifi_only);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -420,20 +339,16 @@
         const_cast<char*>("bugreport"),
         const_cast<char*>("-d"),
         const_cast<char*>("-p"),
-        const_cast<char*>("-B"),
         const_cast<char*>("-z"),
     };
     // clang-format on
-
-    property_set("dumpstate.options", "");
-
     Dumpstate::RunStatus status = options_.Initialize(ARRAY_SIZE(argv), argv);
 
     EXPECT_EQ(status, Dumpstate::RunStatus::OK);
     EXPECT_TRUE(options_.do_add_date);
     EXPECT_TRUE(options_.do_fb);
     EXPECT_TRUE(options_.do_zip_file);
-    EXPECT_TRUE(options_.do_broadcast);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 
     // Other options retain default values
     EXPECT_TRUE(options_.do_vibrate);
@@ -472,7 +387,7 @@
     EXPECT_FALSE(options_.do_fb);
     EXPECT_FALSE(options_.do_progress_updates);
     EXPECT_FALSE(options_.is_remote_mode);
-    EXPECT_FALSE(options_.do_broadcast);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializePartial2) {
@@ -484,7 +399,6 @@
         const_cast<char*>("-p"),
         const_cast<char*>("-P"),
         const_cast<char*>("-R"),
-        const_cast<char*>("-B"),
     };
     // clang-format on
 
@@ -496,13 +410,13 @@
     EXPECT_TRUE(options_.do_fb);
     EXPECT_TRUE(options_.do_progress_updates);
     EXPECT_TRUE(options_.is_remote_mode);
-    EXPECT_TRUE(options_.do_broadcast);
 
     // Other options retain default values
     EXPECT_FALSE(options_.do_add_date);
     EXPECT_FALSE(options_.do_zip_file);
     EXPECT_FALSE(options_.use_socket);
     EXPECT_FALSE(options_.use_control_socket);
+    EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
 }
 
 TEST_F(DumpOptionsTest, InitializeHelp) {
@@ -544,7 +458,7 @@
 }
 
 TEST_F(DumpOptionsTest, ValidateOptionsNeedOutfile2) {
-    options_.do_broadcast = true;
+    options_.do_progress_updates = true;
     // Writing to socket = !writing to file.
     options_.use_socket = true;
     EXPECT_FALSE(options_.ValidateOptions());
@@ -561,19 +475,10 @@
     EXPECT_TRUE(options_.ValidateOptions());
 }
 
-TEST_F(DumpOptionsTest, ValidateOptionsUpdateProgressNeedsBroadcast) {
-    options_.do_progress_updates = true;
-    EXPECT_FALSE(options_.ValidateOptions());
-
-    options_.do_broadcast = true;
-    EXPECT_TRUE(options_.ValidateOptions());
-}
-
 TEST_F(DumpOptionsTest, ValidateOptionsRemoteMode) {
     options_.is_remote_mode = true;
     EXPECT_FALSE(options_.ValidateOptions());
 
-    options_.do_broadcast = true;
     options_.do_zip_file = true;
     options_.do_add_date = true;
     EXPECT_TRUE(options_.ValidateOptions());
@@ -616,8 +521,8 @@
         ds.progress_.reset(new Progress(initial_max, progress, 1.2));
     }
 
-    std::string GetProgressMessage(const std::string& listener_name, int progress, int max,
-                                   int old_max = 0, bool update_progress = true) {
+    std::string GetProgressMessage(int progress, int max,
+            int old_max = 0, bool update_progress = true) {
         EXPECT_EQ(progress, ds.progress_->Get()) << "invalid progress";
         EXPECT_EQ(max, ds.progress_->GetMax()) << "invalid max";
 
@@ -630,9 +535,8 @@
         }
 
         if (update_progress) {
-            message += android::base::StringPrintf("Setting progress (%s): %d/%d (%d%%)\n",
-                                                   listener_name.c_str(), progress, max,
-                                                   (100 * progress / max));
+            message += android::base::StringPrintf("Setting progress: %d/%d (%d%%)\n",
+                                                   progress, max, (100 * progress / max));
         }
 
         return message;
@@ -787,18 +691,17 @@
 TEST_F(DumpstateTest, RunCommandProgress) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     ds.listener_ = listener;
-    ds.listener_name_ = "FoxMulder";
     SetProgress(0, 30);
 
     EXPECT_CALL(*listener, onProgress(66));  // 20/30 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build()));
-    std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
+    std::string progress_message = GetProgressMessage(20, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
     EXPECT_CALL(*listener, onProgress(80));  // 24/30 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build()));
-    progress_message = GetProgressMessage(ds.listener_name_, 24, 30);
+    progress_message = GetProgressMessage(24, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
@@ -806,20 +709,20 @@
     SetDryRun(true);
     EXPECT_CALL(*listener, onProgress(90));  // 27/30 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build()));
-    progress_message = GetProgressMessage(ds.listener_name_, 27, 30);
+    progress_message = GetProgressMessage(27, 30);
     EXPECT_THAT(out, IsEmpty());
     EXPECT_THAT(err, StrEq(progress_message));
 
     SetDryRun(false);
     EXPECT_CALL(*listener, onProgress(96));  // 29/30 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(2).Build()));
-    progress_message = GetProgressMessage(ds.listener_name_, 29, 30);
+    progress_message = GetProgressMessage(29, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
     EXPECT_CALL(*listener, onProgress(100));  // 30/30 %
     EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build()));
-    progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
+    progress_message = GetProgressMessage(30, 30);
     EXPECT_THAT(out, StrEq("stdout\n"));
     EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
 
@@ -1044,14 +947,12 @@
 TEST_F(DumpstateTest, DumpFileUpdateProgress) {
     sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
     ds.listener_ = listener;
-    ds.listener_name_ = "FoxMulder";
     SetProgress(0, 30);
 
     EXPECT_CALL(*listener, onProgress(16));  // 5/30 %
     EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt"));
 
-    std::string progress_message =
-        GetProgressMessage(ds.listener_name_, 5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
+    std::string progress_message = GetProgressMessage(5, 30);  // TODO: unhardcode WEIGHT_FILE (5)?
     EXPECT_THAT(err, StrEq(progress_message));
     EXPECT_THAT(out, StrEq("I AM LINE1\n"));  // dumpstate adds missing newline
 
@@ -1063,48 +964,6 @@
     DumpstateService dss;
 };
 
-TEST_F(DumpstateServiceTest, SetListenerNoName) {
-    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
-    sp<IDumpstateToken> token;
-    EXPECT_TRUE(dss.setListener("", listener, /* getSectionDetails = */ false, &token).isOk());
-    ASSERT_THAT(token, IsNull());
-}
-
-TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
-    sp<IDumpstateToken> token;
-    EXPECT_TRUE(
-        dss.setListener("whatever", nullptr, /* getSectionDetails = */ false, &token).isOk());
-    ASSERT_THAT(token, IsNull());
-}
-
-TEST_F(DumpstateServiceTest, SetListenerTwice) {
-    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
-    sp<IDumpstateToken> token;
-    EXPECT_TRUE(
-        dss.setListener("whatever", listener, /* getSectionDetails = */ false, &token).isOk());
-    ASSERT_THAT(token, NotNull());
-    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
-    EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
-
-    token.clear();
-    EXPECT_TRUE(
-        dss.setListener("whatsoever", listener, /* getSectionDetails = */ false, &token).isOk());
-    ASSERT_THAT(token, IsNull());
-    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
-    EXPECT_FALSE(Dumpstate::GetInstance().report_section_);
-}
-
-TEST_F(DumpstateServiceTest, SetListenerWithSectionDetails) {
-    sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
-    sp<IDumpstateToken> token;
-    Dumpstate::GetInstance().listener_ = nullptr;
-    EXPECT_TRUE(
-        dss.setListener("whatever", listener, /* getSectionDetails = */ true, &token).isOk());
-    ASSERT_THAT(token, NotNull());
-    EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
-    EXPECT_TRUE(Dumpstate::GetInstance().report_section_);
-}
-
 class ProgressTest : public DumpstateBaseTest {
   public:
     Progress GetInstance(int32_t max, double growth_factor, const std::string& path = "") {
diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp
index d398559..3a3df08 100644
--- a/cmds/flatland/GLHelper.cpp
+++ b/cmds/flatland/GLHelper.cpp
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-#include <ui/DisplayInfo.h>
-#include <gui/SurfaceComposerClient.h>
-
 #include "GLHelper.h"
 
- namespace android {
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+
+namespace android {
 
 GLHelper::GLHelper() :
     mDisplay(EGL_NO_DISPLAY),
@@ -228,15 +227,15 @@
         return false;
     }
 
-    DisplayInfo info;
-    status_t err = mSurfaceComposerClient->getDisplayInfo(dpy, &info);
+    DisplayConfig config;
+    status_t err = mSurfaceComposerClient->getActiveDisplayConfig(dpy, &config);
     if (err != NO_ERROR) {
-        fprintf(stderr, "SurfaceComposer::getDisplayInfo failed: %#x\n", err);
+        fprintf(stderr, "SurfaceComposer::getActiveDisplayConfig failed: %#x\n", err);
         return false;
     }
 
-    float scaleX = float(info.w) / float(w);
-    float scaleY = float(info.h) / float(h);
+    float scaleX = static_cast<float>(config.resolution.getWidth()) / w;
+    float scaleY = static_cast<float>(config.resolution.getHeight()) / h;
     *scale = scaleX < scaleY ? scaleX : scaleY;
 
     return true;
diff --git a/cmds/installd/.gitignore b/cmds/installd/.gitignore
new file mode 100644
index 0000000..abc921c
--- /dev/null
+++ b/cmds/installd/.gitignore
@@ -0,0 +1,2 @@
+# ignore the files generated by intellij
+.idea
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 75dec37..8ff4dd8 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -13,6 +13,7 @@
     srcs: [
         "CacheItem.cpp",
         "CacheTracker.cpp",
+        "CrateManager.cpp",
         "InstalldNativeService.cpp",
         "QuotaUtils.cpp",
         "dexopt.cpp",
@@ -163,6 +164,7 @@
     name: "installd_aidl",
     srcs: [
         "binder/android/os/IInstalld.aidl",
+        "binder/android/os/storage/CrateMetadata.aidl",
     ],
     path: "binder",
 }
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
new file mode 100644
index 0000000..6e079eb
--- /dev/null
+++ b/cmds/installd/CrateManager.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 "CrateManager.h"
+
+#ifdef ENABLE_STORAGE_CRATES
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <string>
+#include <utils.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CrateManager::CrateManager(const char* uuid, userid_t userId, const std::string& packageName) {
+    mPackageName = packageName;
+    mRoot = create_data_user_ce_package_path(uuid, userId, (const char*)packageName.c_str());
+    mCratedFoldersRoot = StringPrintf("%s/crates", mRoot.c_str());
+}
+
+CrateManager::~CrateManager() {}
+
+static std::string getValidatedCratedPath(std::string path) {
+    size_t pos = path.rfind("/");
+    if (pos == std::string::npos) {
+        return path;
+    }
+
+    return path.substr(pos + 1, path.length());
+}
+
+void CrateManager::traverseChildDir(const std::string& targetDir,
+    std::function<void(FTSENT*)>& onVisitChildDir) {
+    char* argv[] = {(char*)targetDir.c_str(), nullptr};
+    FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+    if (fts == nullptr) {
+        PLOG(WARNING) << "Failed to fts_open " << targetDir;
+        return;
+    }
+
+    FTSENT* p;
+    while ((p = fts_read(fts)) != nullptr) {
+        switch (p->fts_info) {
+            case FTS_D:
+                if (p->fts_level == 1) {
+                    onVisitChildDir(p);
+                }
+                break;
+            default:
+                break;
+        }
+
+        if (p->fts_level == 1) {
+            fts_set(fts, p, FTS_SKIP);
+        }
+    }
+    fts_close(fts);
+}
+
+void CrateManager::traverseAllPackagesForUser(
+        const std::unique_ptr<std::string>& uuid, userid_t userId,
+        std::function<void(FTSENT*)>& onHandlingPackage) {
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    auto ce_path = create_data_user_ce_path(uuid_, userId);
+    traverseChildDir(ce_path, onHandlingPackage);
+}
+
+void CrateManager::createCrate(
+        CratedFolder cratedFolder,
+        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+    const char* path = cratedFolder->fts_path;
+    if (path == nullptr || *path == '\0') {
+        return;
+    }
+
+    std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
+    crateMetadata->uid = cratedFolder->fts_statp->st_uid;
+    crateMetadata->packageName = mPackageName;
+    crateMetadata->id = getValidatedCratedPath(path);
+
+    onCreateCrate(cratedFolder, crateMetadata);
+}
+
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+    std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
+        createCrate(cratedFolder, onCreateCrate);
+    };
+    traverseChildDir(mCratedFoldersRoot, onVisitCrateDir);
+}
+
+#if CRATE_DEBUG
+void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+    LOG(DEBUG) << "CrateMetadata = {"
+            << "uid : \"" << CrateMetadata->uid
+            << "\", packageName : \"" << CrateMetadata->packageName
+            << "\", id : \"" << CrateMetadata->id
+            << "\"}";
+}
+#endif
+
+} // namespace installd
+} // namespace android
+
+#endif // ENABLE_STORAGE_CRATES
\ No newline at end of file
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
new file mode 100644
index 0000000..4332d4c
--- /dev/null
+++ b/cmds/installd/CrateManager.h
@@ -0,0 +1,89 @@
+/*
+ * 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_INSTALLD_CRATE_INFO_MANAGER_H
+#define ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+
+#ifdef ENABLE_STORAGE_CRATES
+
+#include <android/os/storage/CrateMetadata.h>
+#include <cutils/multiuser.h>
+#include <fts.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#ifndef CRATE_DEBUG
+#define CRATE_DEBUG 1
+#endif
+
+namespace android {
+namespace installd {
+
+using android::os::storage::CrateMetadata;
+
+/**
+ * The crated folder actually is a folder that is the first level child director. In order to
+ * distingish between the crated folder and the other FTSENT*, to define the type "CratedFolder"
+ * make the code easy to identify the difference.
+ */
+typedef FTSENT* CratedFolder;
+
+/**
+ * In order to give the users more fine-grained files controlling, the crate information can help
+ * applications' developers to show the more detail information to the users. The crate information
+ * include the Label, Expiration etc..
+ */
+class CrateManager {
+public:
+    CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
+    ~CrateManager();
+
+    void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+
+    static void traverseChildDir(const std::string& targetDir,
+            std::function<void(FTSENT*)>& onVisitChildDir);
+
+    static void traverseAllPackagesForUser(
+        const std::unique_ptr<std::string>& uuid,
+        userid_t userId,
+        std::function<void(FTSENT*)>& onHandlingPackage);
+
+#if CRATE_DEBUG
+    static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+#endif
+private:
+    std::string mRoot;
+    std::string mCratedFoldersRoot;
+    std::string mPackageName;
+
+    void createCrate(
+        CratedFolder cratedFolder,
+        std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+};
+
+} // namespace installd
+} // namespace android
+
+#else // ENABLE_STORAGE_CRATES
+#include <android/os/storage/CrateMetadata.h>
+using android::os::storage::CrateMetadata;
+#endif // ENABLE_STORAGE_CRATES
+
+#endif // ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 0fde31a..3713e87 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -31,6 +31,7 @@
 #include <sys/file.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
+#include <sys/mount.h>
 #include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
@@ -65,6 +66,7 @@
 #include "view_compiler.h"
 
 #include "CacheTracker.h"
+#include "CrateManager.h"
 #include "MatchExtensionGen.h"
 #include "QuotaUtils.h"
 
@@ -86,16 +88,15 @@
 static constexpr const char* kCpPath = "/system/bin/cp";
 static constexpr const char* kXattrDefault = "user.default";
 
+static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
+static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+
 static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
 
 static constexpr const char* PKG_LIB_POSTFIX = "/lib";
 static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
 static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
 
-static constexpr const char *kIdMapPath = "/system/bin/idmap";
-static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
-static constexpr const char* IDMAP_SUFFIX = "@idmap";
-
 // fsverity assumes the page size is always 4096. If not, the feature can not be
 // enabled.
 static constexpr int kVerityPageSize = 4096;
@@ -103,9 +104,15 @@
 static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
 static constexpr const char* kFuseProp = "persist.sys.fuse";
 
+/**
+ * Property to control if app data isolation is enabled.
+ */
+static constexpr const char* kAppDataIsolationEnabledProperty = "persist.zygote.app_data_isolation";
 static constexpr const char* kMntSdcardfs = "/mnt/runtime/default/";
 static constexpr const char* kMntFuse = "/mnt/pass_through/0/";
 
+static std::atomic<bool> sAppDataIsolationEnabled(false);
+
 namespace {
 
 constexpr const char* kDump = "android.permission.DUMP";
@@ -265,6 +272,8 @@
     sp<ProcessState> ps(ProcessState::self());
     ps->startThreadPool();
     ps->giveThreadPoolName();
+    sAppDataIsolationEnabled = android::base::GetBoolProperty(
+            kAppDataIsolationEnabledProperty, true);
     return android::OK;
 }
 
@@ -457,9 +466,12 @@
 
         // And return the CE inode of the top-level data directory so we can
         // clear contents while CE storage is locked
-        if ((_aidl_return != nullptr)
-                && get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return)) != 0) {
-            return error("Failed to get_path_inode for " + path);
+        if (_aidl_return != nullptr) {
+            ino_t result;
+            if (get_path_inode(path, &result) != 0) {
+                return error("Failed to get_path_inode for " + path);
+            }
+            *_aidl_return = static_cast<uint64_t>(result);
         }
     }
     if (flags & FLAG_STORAGE_DE) {
@@ -1090,7 +1102,7 @@
 binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
         const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
         const std::string& dataAppName, int32_t appId, const std::string& seInfo,
-        int32_t targetSdkVersion) {
+        int32_t targetSdkVersion, const std::string& fromCodePath) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(fromUuid);
     CHECK_ARGUMENT_UUID(toUuid);
@@ -1107,13 +1119,12 @@
 
     // Copy app
     {
-        auto from = create_data_app_package_path(from_uuid, data_app_name);
         auto to = create_data_app_package_path(to_uuid, data_app_name);
         auto to_parent = create_data_app_path(to_uuid);
 
-        int rc = copy_directory_recursive(from.c_str(), to_parent.c_str());
+        int rc = copy_directory_recursive(fromCodePath.c_str(), to_parent.c_str());
         if (rc != 0) {
-            res = error(rc, "Failed copying " + from + " to " + to);
+            res = error(rc, "Failed copying " + fromCodePath + " to " + to);
             goto fail;
         }
 
@@ -2034,6 +2045,100 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::getAppCrates(
+        const std::unique_ptr<std::string>& uuid,
+        const std::vector<std::string>& packageNames, int32_t userId,
+        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    for (const auto& packageName : packageNames) {
+        CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    }
+#ifdef ENABLE_STORAGE_CRATES
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+        if (cratedFolder == nullptr) {
+            return;
+        }
+        retVector->push_back(std::move(crateMetadata));
+    };
+
+    for (const auto& packageName : packageNames) {
+#if CRATE_DEBUG
+        LOG(DEBUG) << "packageName = " << packageName;
+#endif
+        auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageName);
+        crateManager->traverseAllCrates(onCreateCrate);
+    }
+
+#if CRATE_DEBUG
+    LOG(WARNING) << "retVector->size() =" << retVector->size();
+    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+        CrateManager::dump(*iter);
+    }
+#endif
+
+    *_aidl_return = std::move(retVector);
+#else // ENABLE_STORAGE_CRATES
+    *_aidl_return = nullptr;
+
+    /* prevent compile warning fail */
+    if (userId < 0) {
+        return error();
+    }
+#endif // ENABLE_STORAGE_CRATES
+    return ok();
+}
+
+binder::Status InstalldNativeService::getUserCrates(
+        const std::unique_ptr<std::string>& uuid, int32_t userId,
+        std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+#ifdef ENABLE_STORAGE_CRATES
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+    auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+
+    std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+            [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+        if (cratedFolder == nullptr) {
+            return;
+        }
+        retVector->push_back(std::move(crateMetadata));
+    };
+
+    std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
+        auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageDir->fts_name);
+        crateManager->traverseAllCrates(onCreateCrate);
+    };
+    CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
+
+#if CRATE_DEBUG
+    LOG(DEBUG) << "retVector->size() =" << retVector->size();
+    for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+        CrateManager::dump(*iter);
+    }
+#endif
+
+    *_aidl_return = std::move(retVector);
+#else // ENABLE_STORAGE_CRATES
+    *_aidl_return = nullptr;
+
+    /* prevent compile warning fail */
+    if (userId < 0) {
+        return error();
+    }
+#endif // ENABLE_STORAGE_CRATES
+    return ok();
+}
+
 binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
         int32_t userId, int32_t appId, int64_t cacheQuota) {
     ENFORCE_UID(AID_SYSTEM);
@@ -2162,26 +2267,6 @@
     return *_aidl_return ? ok() : error("viewcompiler failed");
 }
 
-binder::Status InstalldNativeService::markBootComplete(const std::string& instructionSet) {
-    ENFORCE_UID(AID_SYSTEM);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
-
-    const char* instruction_set = instructionSet.c_str();
-
-    char boot_marker_path[PKG_PATH_MAX];
-    sprintf(boot_marker_path,
-          "%s/%s/%s/.booting",
-          android_data_dir.c_str(),
-          DALVIK_CACHE,
-          instruction_set);
-
-    ALOGV("mark_boot_complete : %s", boot_marker_path);
-    if (unlink(boot_marker_path) != 0) {
-        return error(StringPrintf("Failed to unlink %s", boot_marker_path));
-    }
-    return ok();
-}
-
 binder::Status InstalldNativeService::linkNativeLibraryDirectory(
         const std::unique_ptr<std::string>& uuid, const std::string& packageName,
         const std::string& nativeLibPath32, int32_t userId) {
@@ -2274,206 +2359,6 @@
     return res;
 }
 
-static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
-{
-    execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk,
-            StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr);
-    PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
-}
-
-static void run_verify_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
-{
-    execl(kIdMapPath, kIdMapPath, "--verify", target_apk, overlay_apk,
-            StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr);
-    PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
-}
-
-static bool delete_stale_idmap(const char* target_apk, const char* overlay_apk,
-        const char* idmap_path, int32_t uid) {
-    int idmap_fd = open(idmap_path, O_RDWR);
-    if (idmap_fd < 0) {
-        PLOG(ERROR) << "idmap open failed: " << idmap_path;
-        unlink(idmap_path);
-        return true;
-    }
-
-    pid_t pid;
-    pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        if (setgid(uid) != 0) {
-            LOG(ERROR) << "setgid(" << uid << ") failed during idmap";
-            exit(1);
-        }
-        if (setuid(uid) != 0) {
-            LOG(ERROR) << "setuid(" << uid << ") failed during idmap";
-            exit(1);
-        }
-        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
-            PLOG(ERROR) << "flock(" << idmap_path << ") failed during idmap";
-            exit(1);
-        }
-
-        run_verify_idmap(target_apk, overlay_apk, idmap_fd);
-        exit(1); /* only if exec call to deleting stale idmap failed */
-    } else {
-        int status = wait_child(pid);
-        close(idmap_fd);
-
-        if (status != 0) {
-            // Failed on verifying if idmap is made from target_apk and overlay_apk.
-            LOG(DEBUG) << "delete stale idmap: " << idmap_path;
-            unlink(idmap_path);
-            return true;
-        }
-    }
-    return false;
-}
-
-// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
-// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
-static int flatten_path(const char *prefix, const char *suffix,
-        const char *overlay_path, char *idmap_path, size_t N)
-{
-    if (overlay_path == nullptr || idmap_path == nullptr) {
-        return -1;
-    }
-    const size_t len_overlay_path = strlen(overlay_path);
-    // will access overlay_path + 1 further below; requires absolute path
-    if (len_overlay_path < 2 || *overlay_path != '/') {
-        return -1;
-    }
-    const size_t len_idmap_root = strlen(prefix);
-    const size_t len_suffix = strlen(suffix);
-    if (SIZE_MAX - len_idmap_root < len_overlay_path ||
-            SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
-        // additions below would cause overflow
-        return -1;
-    }
-    if (N < len_idmap_root + len_overlay_path + len_suffix) {
-        return -1;
-    }
-    memset(idmap_path, 0, N);
-    snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
-    char *ch = idmap_path + len_idmap_root;
-    while (*ch != '\0') {
-        if (*ch == '/') {
-            *ch = '@';
-        }
-        ++ch;
-    }
-    return 0;
-}
-
-binder::Status InstalldNativeService::idmap(const std::string& targetApkPath,
-        const std::string& overlayApkPath, int32_t uid) {
-    ENFORCE_UID(AID_SYSTEM);
-    CHECK_ARGUMENT_PATH(targetApkPath);
-    CHECK_ARGUMENT_PATH(overlayApkPath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
-
-    const char* target_apk = targetApkPath.c_str();
-    const char* overlay_apk = overlayApkPath.c_str();
-    ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
-
-    int idmap_fd = -1;
-    char idmap_path[PATH_MAX];
-    struct stat idmap_stat;
-    bool outdated = false;
-
-    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
-                idmap_path, sizeof(idmap_path)) == -1) {
-        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
-        goto fail;
-    }
-
-    if (stat(idmap_path, &idmap_stat) < 0) {
-        outdated = true;
-    } else {
-        outdated = delete_stale_idmap(target_apk, overlay_apk, idmap_path, uid);
-    }
-
-    if (outdated) {
-        idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
-    } else {
-        idmap_fd = open(idmap_path, O_RDWR);
-    }
-
-    if (idmap_fd < 0) {
-        ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
-        goto fail;
-    }
-    if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
-        ALOGE("idmap cannot chown '%s'\n", idmap_path);
-        goto fail;
-    }
-    if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
-        ALOGE("idmap cannot chmod '%s'\n", idmap_path);
-        goto fail;
-    }
-
-    if (!outdated) {
-        close(idmap_fd);
-        return ok();
-    }
-
-    pid_t pid;
-    pid = fork();
-    if (pid == 0) {
-        /* child -- drop privileges before continuing */
-        if (setgid(uid) != 0) {
-            ALOGE("setgid(%d) failed during idmap\n", uid);
-            exit(1);
-        }
-        if (setuid(uid) != 0) {
-            ALOGE("setuid(%d) failed during idmap\n", uid);
-            exit(1);
-        }
-        if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
-            ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
-            exit(1);
-        }
-
-        run_idmap(target_apk, overlay_apk, idmap_fd);
-        exit(1); /* only if exec call to idmap failed */
-    } else {
-        int status = wait_child(pid);
-        if (status != 0) {
-            ALOGE("idmap failed, status=0x%04x\n", status);
-            goto fail;
-        }
-    }
-
-    close(idmap_fd);
-    return ok();
-fail:
-    if (idmap_fd >= 0) {
-        close(idmap_fd);
-        unlink(idmap_path);
-    }
-    return error();
-}
-
-binder::Status InstalldNativeService::removeIdmap(const std::string& overlayApkPath) {
-    ENFORCE_UID(AID_SYSTEM);
-    CHECK_ARGUMENT_PATH(overlayApkPath);
-    std::lock_guard<std::recursive_mutex> lock(mLock);
-
-    const char* overlay_apk = overlayApkPath.c_str();
-    char idmap_path[PATH_MAX];
-
-    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
-                idmap_path, sizeof(idmap_path)) == -1) {
-        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
-        return error();
-    }
-    if (unlink(idmap_path) < 0) {
-        ALOGE("couldn't unlink idmap file %s\n", idmap_path);
-        return error();
-    }
-    return ok();
-}
-
 binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
         const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
         const std::string& seInfo) {
@@ -2816,6 +2701,115 @@
     return ok();
 }
 
+// Mount volume's CE and DE storage to mirror
+binder::Status InstalldNativeService::tryMountDataMirror(
+        const std::unique_ptr<std::string>& uuid) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    if (!sAppDataIsolationEnabled) {
+        return ok();
+    }
+    if (!uuid) {
+        return error("Should not happen, mounting uuid == null");
+    }
+
+    const char* uuid_ = uuid->c_str();
+
+    std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+    if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create CE mirror");
+    }
+
+    std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+    if (fs_prepare_dir(mirrorVolDePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
+        return error("Failed to create DE mirror");
+    }
+
+    auto cePath = StringPrintf("%s/user", create_data_path(uuid_).c_str());
+    auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+
+    if (access(cePath.c_str(), F_OK) != 0) {
+        return error("Cannot access CE path: " + cePath);
+    }
+    if (access(dePath.c_str(), F_OK) != 0) {
+        return error("Cannot access DE path: " + dePath);
+    }
+
+    struct stat ceStat, mirrorCeStat;
+    if (stat(cePath.c_str(), &ceStat) != 0) {
+        return error("Failed to stat " + cePath);
+    }
+    if (stat(mirrorVolCePath.c_str(), &mirrorCeStat) != 0) {
+        return error("Failed to stat " + mirrorVolCePath);
+    }
+
+    if (mirrorCeStat.st_ino == ceStat.st_ino) {
+        // As it's being called by prepareUserStorage, it can be called multiple times.
+        // Hence, we if we mount it already, we should skip it.
+        LOG(WARNING) << "CE dir is mounted already: " + cePath;
+        return ok();
+    }
+
+    // Mount CE mirror
+    if (TEMP_FAILURE_RETRY(mount(cePath.c_str(), mirrorVolCePath.c_str(), NULL,
+            MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolCePath);
+    }
+
+    // Mount DE mirror
+    if (TEMP_FAILURE_RETRY(mount(dePath.c_str(), mirrorVolDePath.c_str(), NULL,
+            MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+        return error("Failed to mount " + mirrorVolDePath);
+    }
+    return ok();
+}
+
+// Unmount volume's CE and DE storage from mirror
+binder::Status InstalldNativeService::onPrivateVolumeRemoved(
+        const std::unique_ptr<std::string>& uuid) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(uuid);
+    if (!sAppDataIsolationEnabled) {
+        return ok();
+    }
+    if (!uuid) {
+        // It happens when private volume failed to mount.
+        LOG(INFO) << "Ignore unmount uuid=null";
+        return ok();
+    }
+    const char* uuid_ = uuid->c_str();
+
+    binder::Status res = ok();
+
+    std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+    std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+
+    // Unmount CE storage
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+    if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(),
+                                strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorCeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorCeVolPath);
+    }
+
+    // Unmount DE storage
+    if (TEMP_FAILURE_RETRY(umount(mirrorDeVolPath.c_str())) != 0) {
+        if (errno != ENOENT) {
+            res = error(StringPrintf("Failed to umount %s %s", mirrorDeVolPath.c_str(),
+                                strerror(errno)));
+        }
+    }
+    if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
+        res = error("Failed to delete " + mirrorDeVolPath);
+    }
+    return res;
+}
+
 std::string InstalldNativeService::findDataMediaPath(
         const std::unique_ptr<std::string>& uuid, userid_t userid) {
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 149936d..27c59b0 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -81,13 +81,23 @@
             int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
             std::vector<int64_t>* _aidl_return);
 
+    binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+            const std::vector<std::string>& packageNames,
+            int32_t userId,
+            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+                    _aidl_return);
+    binder::Status getUserCrates(
+            const std::unique_ptr<std::string>& uuid, int32_t userId,
+            std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+                    _aidl_return);
+
     binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
             int32_t userId, int32_t appId, int64_t cacheQuota);
 
     binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid,
             const std::unique_ptr<std::string>& toUuid, const std::string& packageName,
             const std::string& dataAppName, int32_t appId, const std::string& seInfo,
-            int32_t targetSdkVersion);
+            int32_t targetSdkVersion, const std::string& fromCodePath);
 
     binder::Status dexopt(const std::string& apkPath, int32_t uid,
             const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
@@ -119,11 +129,7 @@
     binder::Status destroyProfileSnapshot(const std::string& packageName,
             const std::string& profileName);
 
-    binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
-            int32_t uid);
-    binder::Status removeIdmap(const std::string& overlayApkPath);
     binder::Status rmPackageDir(const std::string& packageDir);
-    binder::Status markBootComplete(const std::string& instructionSet);
     binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
             int64_t cacheReservedBytes, int32_t flags);
     binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid,
@@ -149,6 +155,8 @@
     binder::Status invalidateMounts();
     binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
             bool* _aidl_return);
+    binder::Status tryMountDataMirror(const std::unique_ptr<std::string>& volumeUuid);
+    binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
 
     binder::Status prepareAppProfile(const std::string& packageName,
             int32_t userId, int32_t appId, const std::string& profileName,
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp
index f2abf3a..e080291 100644
--- a/cmds/installd/QuotaUtils.cpp
+++ b/cmds/installd/QuotaUtils.cpp
@@ -61,6 +61,10 @@
         std::getline(in, target, ' ');
         std::getline(in, ignored);
 
+        if (target.compare(0, 13, "/data_mirror/") == 0) {
+            continue;
+        }
+
         if (source.compare(0, 11, "/dev/block/") == 0) {
             struct dqblk dq;
             if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index d99bcc8..f2e86ba 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -40,11 +40,19 @@
     long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
     long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
 
+    @nullable
+    android.os.storage.CrateMetadata[] getAppCrates(
+            @nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+             int userId);
+    @nullable
+    android.os.storage.CrateMetadata[] getUserCrates(
+            @nullable @utf8InCpp String uuid, int userId);
+
     void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
 
     void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
             @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId,
-            @utf8InCpp String seInfo, int targetSdkVersion);
+            @utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
 
     void dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
             @utf8InCpp String instructionSet, int dexoptNeeded,
@@ -72,10 +80,7 @@
             @utf8InCpp String profileName, @utf8InCpp String classpath);
     void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
 
-    void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
-    void removeIdmap(@utf8InCpp String overlayApkPath);
     void rmPackageDir(@utf8InCpp String packageDir);
-    void markBootComplete(@utf8InCpp String instructionSet);
     void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
             long cacheReservedBytes, int flags);
     void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
@@ -111,6 +116,8 @@
             int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
     void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
             int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
+    void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid);
+    void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid);
 
     void migrateLegacyObbData();
 
diff --git a/cmds/installd/binder/android/os/storage/CrateMetadata.aidl b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
new file mode 100644
index 0000000..bd6d12d
--- /dev/null
+++ b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
@@ -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.
+ */
+package android.os.storage;
+
+/** {@hide} */
+parcelable CrateMetadata {
+    /**
+     * To tell which uid the crate belong to.
+     * <p>Because installd query all of crates in specified userId, the install may return the list
+     * whose elements have the same crate id but different uid and package name.
+     * It needs to tell the caller the difference between these elements.
+     */
+    int uid;
+
+    /**
+     * To tell which the package the crate belong to.
+     * <p>Because installd query all of crates in specified uid, the install may return the list
+     * whose elements have the same uid and crate id but different package name.
+     * It needs to tell the caller the difference between these elements.
+     */
+    @utf8InCpp String packageName;
+
+    /**
+     * To tell the crate id that is the child directory/folder name in crates
+     * root.
+     */
+    @utf8InCpp String id;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 2f79552..939cf90 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -278,6 +278,15 @@
     return "/data/dalvik-cache";
 }
 
+std::string create_system_user_ce_path(userid_t userId) {
+    return StringPrintf("%s/system_ce/%u", create_data_path(nullptr).c_str(), userId);
+}
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name) {
+    check_package_name(package_name);
+    return StringPrintf("%s/%s", create_system_user_ce_path(userId).c_str(), package_name);
+}
+
 // Keep profile paths in sync with ActivityThread and LoadedApk.
 const std::string PROFILE_EXT = ".prof";
 const std::string CURRENT_PROFILE_EXT = ".cur";
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 6a42026..6a39adc 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -82,6 +82,10 @@
 
 std::string create_data_dalvik_cache_path();
 
+std::string create_system_user_ce_path(userid_t userId);
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name);
+
 std::string create_primary_cur_profile_dir_path(userid_t userid);
 std::string create_primary_current_profile_package_dir_path(
         userid_t user, const std::string& package_name);
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index c70bc3e..b574098 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -1,5 +1,6 @@
 syntax = "proto2";
 option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
 
 message Trace {
     repeated Increment increment = 1;
@@ -46,6 +47,12 @@
         SecureFlagChange            secure_flag             = 14;
         DeferredTransactionChange   deferred_transaction    = 15;
         CornerRadiusChange          corner_radius           = 16;
+        ReparentChange              reparent                = 17;
+        RelativeParentChange        relative_parent         = 18;
+        DetachChildrenChange        detach_children         = 19;
+        ReparentChildrenChange      reparent_children       = 20;
+        BackgroundBlurRadiusChange  background_blur_radius  = 21;
+        ShadowRadiusChange          shadow_radius           = 22;
     }
 }
 
@@ -67,6 +74,10 @@
     required float corner_radius = 1;
 }
 
+message BackgroundBlurRadiusChange {
+    required float background_blur_radius = 1;
+}
+
 message LayerChange {
     required uint32 layer = 1;
 }
@@ -177,3 +188,24 @@
     required int32  id   = 1;
     required int32  mode = 2;
 }
+
+message ReparentChange {
+    required int32 parent_id = 1;
+}
+
+message ReparentChildrenChange {
+    required int32 parent_id = 1;
+}
+
+message RelativeParentChange {
+    required int32 relative_parent_id = 1;
+    required int32 z = 2;
+}
+
+message DetachChildrenChange {
+    required bool detach_children = 1;
+}
+
+message ShadowRadiusChange {
+    required float radius = 1;
+}
\ No newline at end of file
diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp
index 390d398..64db5f0 100644
--- a/cmds/surfacereplayer/replayer/Event.cpp
+++ b/cmds/surfacereplayer/replayer/Event.cpp
@@ -17,6 +17,7 @@
 #include "Event.h"
 
 using namespace android;
+using Increment = surfaceflinger::Increment;
 
 Event::Event(Increment::IncrementCase type) : mIncrementType(type) {}
 
diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h
index 44b60f5..09a7c24 100644
--- a/cmds/surfacereplayer/replayer/Event.h
+++ b/cmds/surfacereplayer/replayer/Event.h
@@ -24,6 +24,8 @@
 
 namespace android {
 
+using Increment = surfaceflinger::Increment;
+
 class Event {
   public:
     Event(Increment::IncrementCase);
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index 34886a9..2b5667d 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -412,6 +412,21 @@
                 setDeferredTransaction(transaction, change.id(),
                         change.deferred_transaction());
                 break;
+            case SurfaceChange::SurfaceChangeCase::kReparent:
+                setReparentChange(transaction, change.id(), change.reparent());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kReparentChildren:
+                setReparentChildrenChange(transaction, change.id(), change.reparent_children());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kRelativeParent:
+                setRelativeParentChange(transaction, change.id(), change.relative_parent());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kDetachChildren:
+                setDetachChildrenChange(transaction, change.id(), change.detach_children());
+                break;
+            case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+                setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
+                break;
             default:
                 status = 1;
                 break;
@@ -495,6 +510,14 @@
     t.setCornerRadius(mLayers[id], cc.corner_radius());
 }
 
+void Replayer::setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
+        layer_id id, const BackgroundBlurRadiusChange& cc) {
+    ALOGV("Layer %d: Setting Background Blur Radius -- backgroundBlurRadius=%d", id,
+        cc.background_blur_radius());
+
+    t.setBackgroundBlurRadius(mLayers[id], cc.background_blur_radius());
+}
+
 void Replayer::setMatrix(SurfaceComposerClient::Transaction& t,
         layer_id id, const MatrixChange& mc) {
     ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
@@ -591,7 +614,7 @@
             pc.viewport().bottom());
     Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
 
-    t.setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame);
+    t.setDisplayProjection(mDisplays[id], ui::toRotation(pc.orientation()), viewport, frame);
 }
 
 status_t Replayer::createSurfaceControl(
@@ -680,3 +703,40 @@
     mComposerClient = new SurfaceComposerClient;
     return mComposerClient->initCheck();
 }
+
+void Replayer::setReparentChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const ReparentChange& c) {
+    sp<IBinder> newParentHandle = nullptr;
+    if (mLayers.count(c.parent_id()) != 0 && mLayers[c.parent_id()] != nullptr) {
+        newParentHandle = mLayers[c.parent_id()]->getHandle();
+    }
+    t.reparent(mLayers[id], newParentHandle);
+}
+
+void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const RelativeParentChange& c) {
+    if (mLayers.count(c.relative_parent_id()) == 0 || mLayers[c.relative_parent_id()] == nullptr) {
+        ALOGE("Layer %d not found in set relative parent transaction", c.relative_parent_id());
+        return;
+    }
+    t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()]->getHandle(), c.z());
+}
+
+void Replayer::setDetachChildrenChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const DetachChildrenChange& c) {
+    t.detachChildren(mLayers[id]);
+}
+
+void Replayer::setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const ReparentChildrenChange& c) {
+    if (mLayers.count(c.parent_id()) == 0 || mLayers[c.parent_id()] == nullptr) {
+        ALOGE("Layer %d not found in reparent children transaction", c.parent_id());
+        return;
+    }
+    t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle());
+}
+
+void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+        layer_id id, const ShadowRadiusChange& c) {
+    t.setShadowRadius(mLayers[id], c.radius());
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index ad807ee..95857e1 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -38,6 +38,8 @@
 #include <unordered_map>
 #include <utility>
 
+using namespace android::surfaceflinger;
+
 namespace android {
 
 const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat";
@@ -92,6 +94,8 @@
             layer_id id, const CropChange& cc);
     void setCornerRadius(SurfaceComposerClient::Transaction& t,
             layer_id id, const CornerRadiusChange& cc);
+    void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
+            layer_id id, const BackgroundBlurRadiusChange& cc);
     void setMatrix(SurfaceComposerClient::Transaction& t,
             layer_id id, const MatrixChange& mc);
     void setOverrideScalingMode(SurfaceComposerClient::Transaction& t,
@@ -108,6 +112,16 @@
             layer_id id, const SecureFlagChange& sfc);
     void setDeferredTransaction(SurfaceComposerClient::Transaction& t,
             layer_id id, const DeferredTransactionChange& dtc);
+    void setReparentChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const ReparentChange& c);
+    void setRelativeParentChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const RelativeParentChange& c);
+    void setDetachChildrenChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const DetachChildrenChange& c);
+    void setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const ReparentChildrenChange& c);
+    void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+            layer_id id, const ShadowRadiusChange& c);
 
     void setDisplaySurface(SurfaceComposerClient::Transaction& t,
             display_id id, const DispSurfaceChange& dsc);
diff --git a/data/etc/android.hardware.context_hub.xml b/data/etc/android.hardware.context_hub.xml
new file mode 100644
index 0000000..8133e0b
--- /dev/null
+++ b/data/etc/android.hardware.context_hub.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Features for devices supporting a Context Hub. -->
+<permissions>
+    <feature name="android.hardware.context_hub" />
+</permissions>
diff --git a/data/etc/android.hardware.reboot_escrow.xml b/data/etc/android.hardware.reboot_escrow.xml
new file mode 100644
index 0000000..5db9f4c
--- /dev/null
+++ b/data/etc/android.hardware.reboot_escrow.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<!--
+     This is the feature indicating that the device has support for the
+     RebootEscrow HAL.
+-->
+
+<permissions>
+    <feature name="android.hardware.reboot_escrow" />
+</permissions>
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
new file mode 100644
index 0000000..b6c0765
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
new file mode 100644
index 0000000..4e975e5
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
new file mode 100644
index 0000000..a331095
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
new file mode 100644
index 0000000..41e6668
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png b/docs/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png
new file mode 100644
index 0000000..2633996
--- /dev/null
+++ b/docs/images/camera2/metadata/android.scaler.rotateAndCrop/crop-region-rotate-90-43-ratio.png
Binary files differ
diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h
index 479e9b8..bbb3193 100644
--- a/headers/media_plugin/media/openmax/OMX_IndexExt.h
+++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h
@@ -101,6 +101,7 @@
     OMX_IndexConfigOperatingRate,                   /**< reference: OMX_PARAM_U32TYPE in Q16 format for video and in Hz for audio */
     OMX_IndexParamConsumerUsageBits,                /**< reference: OMX_PARAM_U32TYPE */
     OMX_IndexConfigLatency,                         /**< reference: OMX_PARAM_U32TYPE */
+    OMX_IndexConfigLowLatency,                      /**< reference: OMX_CONFIG_BOOLEANTYPE */
     OMX_IndexExtOtherEndUnused,
 
     /* Time configurations */
diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h
index 435fcc8..dc37bbd 100644
--- a/headers/media_plugin/media/openmax/OMX_VideoExt.h
+++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h
@@ -291,18 +291,19 @@
 
 /** Dolby Vision Profile enum type */
 typedef enum OMX_VIDEO_DOLBYVISIONPROFILETYPE {
-    OMX_VIDEO_DolbyVisionProfileUnknown = 0x0,
-    OMX_VIDEO_DolbyVisionProfileDvavPer = 0x1,
-    OMX_VIDEO_DolbyVisionProfileDvavPen = 0x2,
-    OMX_VIDEO_DolbyVisionProfileDvheDer = 0x4,
-    OMX_VIDEO_DolbyVisionProfileDvheDen = 0x8,
-    OMX_VIDEO_DolbyVisionProfileDvheDtr = 0x10,
-    OMX_VIDEO_DolbyVisionProfileDvheStn = 0x20,
-    OMX_VIDEO_DolbyVisionProfileDvheDth = 0x40,
-    OMX_VIDEO_DolbyVisionProfileDvheDtb = 0x80,
-    OMX_VIDEO_DolbyVisionProfileDvheSt  = 0x100,
-    OMX_VIDEO_DolbyVisionProfileDvavSe  = 0x200,
-    OMX_VIDEO_DolbyVisionProfileMax     = 0x7FFFFFFF
+    OMX_VIDEO_DolbyVisionProfileUnknown  = 0x0,
+    OMX_VIDEO_DolbyVisionProfileDvavPer  = 0x1,
+    OMX_VIDEO_DolbyVisionProfileDvavPen  = 0x2,
+    OMX_VIDEO_DolbyVisionProfileDvheDer  = 0x4,
+    OMX_VIDEO_DolbyVisionProfileDvheDen  = 0x8,
+    OMX_VIDEO_DolbyVisionProfileDvheDtr  = 0x10,
+    OMX_VIDEO_DolbyVisionProfileDvheStn  = 0x20,
+    OMX_VIDEO_DolbyVisionProfileDvheDth  = 0x40,
+    OMX_VIDEO_DolbyVisionProfileDvheDtb  = 0x80,
+    OMX_VIDEO_DolbyVisionProfileDvheSt   = 0x100,
+    OMX_VIDEO_DolbyVisionProfileDvavSe   = 0x200,
+    OMX_VIDEO_DolbyVisionProfileDvav110  = 0x400,
+    OMX_VIDEO_DolbyVisionProfileMax      = 0x7FFFFFFF
 } OMX_VIDEO_DOLBYVISIONPROFILETYPE;
 
 /** Dolby Vision Level enum type */
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 2def64d..727a4af 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -26,6 +26,7 @@
 #ifndef ANDROID_BITMAP_H
 #define ANDROID_BITMAP_H
 
+#include <stdbool.h>
 #include <stdint.h>
 #include <jni.h>
 
@@ -60,6 +61,30 @@
     ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
     /** Alpha: 8 bits. */
     ANDROID_BITMAP_FORMAT_A_8       = 8,
+    /** Each component is stored as a half float. **/
+    ANDROID_BITMAP_FORMAT_RGBA_F16  = 9,
+};
+
+/** Bitmap alpha format */
+enum {
+    /** Pixel components are premultiplied by alpha. */
+    ANDROID_BITMAP_FLAGS_ALPHA_PREMUL   = 0,
+    /** Pixels are opaque. */
+    ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE   = 1,
+    /** Pixel components are independent of alpha. */
+    ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL = 2,
+    /** Bit mask for AndroidBitmapFormat.flags to isolate the alpha. */
+    ANDROID_BITMAP_FLAGS_ALPHA_MASK     = 0x3,
+    /** Shift for AndroidBitmapFormat.flags to isolate the alpha. */
+    ANDROID_BITMAP_FLAGS_ALPHA_SHIFT    = 0,
+};
+
+enum {
+    /** If this bit is set in AndroidBitmapInfo.flags, the Bitmap uses the
+      * HARDWARE Config, and its {@link AHardwareBuffer} can be retrieved via
+      * {@link AndroidBitmap_getHardwareBuffer}.
+      */
+    ANDROID_BITMAP_FLAGS_IS_HARDWARE = 1 << 31,
 };
 
 /** Bitmap info, see AndroidBitmap_getInfo(). */
@@ -72,17 +97,39 @@
     uint32_t    stride;
     /** The bitmap pixel format. See {@link AndroidBitmapFormat} */
     int32_t     format;
-    /** Unused. */
-    uint32_t    flags;      // 0 for now
+    /** Bitfield containing information about the bitmap.
+     *
+     * <p>Two bits are used to encode alpha. Use {@link ANDROID_BITMAP_FLAGS_ALPHA_MASK}
+     * and {@link ANDROID_BITMAP_FLAGS_ALPHA_SHIFT} to retrieve them.</p>
+     *
+     * <p>One bit is used to encode whether the Bitmap uses the HARDWARE Config. Use
+     * {@link ANDROID_BITMAP_FLAGS_IS_HARDWARE} to know.</p>
+     *
+     * <p>These flags were introduced in API level 30.</p>
+     */
+    uint32_t    flags;
 } AndroidBitmapInfo;
 
 /**
- * Given a java bitmap object, fill out the AndroidBitmapInfo struct for it.
+ * Given a java bitmap object, fill out the {@link AndroidBitmapInfo} struct for it.
  * If the call fails, the info parameter will be ignored.
  */
 int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
                           AndroidBitmapInfo* info);
 
+#if __ANDROID_API__ >= 30
+
+/**
+ * Given a java bitmap object, return its {@link ADataSpace}.
+ *
+ * Note that {@link ADataSpace} only exposes a few values. This may return
+ * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no
+ * corresponding ADataSpace.
+ */
+int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap)  __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
 /**
  * Given a java bitmap object, attempt to lock the pixel address.
  * Locking will ensure that the memory for the pixels will not move
@@ -103,6 +150,103 @@
  */
 int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
 
+#if __ANDROID_API__ >= 30
+
+// Note: these values match android.graphics.Bitmap#compressFormat.
+
+/**
+ *  Specifies the formats that can be compressed to with
+ *  {@link AndroidBitmap_compress}.
+ */
+enum AndroidBitmapCompressFormat {
+    /**
+     * Compress to the JPEG format. quality of 0 means
+     * compress for the smallest size. 100 means compress for max
+     * visual quality.
+     */
+    ANDROID_BITMAP_COMPRESS_FORMAT_JPEG = 0,
+    /**
+     * Compress to the PNG format. PNG is lossless, so quality is
+     * ignored.
+     */
+    ANDROID_BITMAP_COMPRESS_FORMAT_PNG = 1,
+    /**
+     * Compress to the WEBP lossy format. quality of 0 means
+     * compress for the smallest size. 100 means compress for max
+     * visual quality.
+     */
+    ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY = 3,
+    /**
+     * Compress to the WEBP lossless format. quality refers to how
+     * much effort to put into compression. A value of 0 means to
+     * compress quickly, resulting in a relatively large file size.
+     * 100 means to spend more time compressing, resulting in a
+     * smaller file.
+     */
+    ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS = 4,
+};
+
+/**
+ *  User-defined function for writing the output of compression.
+ *
+ *  @param userContext Pointer to user-defined data passed to
+ *         {@link AndroidBitmap_compress}.
+ *  @param data Compressed data of |size| bytes to write.
+ *  @param size Length in bytes of data to write.
+ *  @return Whether the operation succeeded.
+ */
+typedef bool (*AndroidBitmap_CompressWriteFunc)(void* userContext,
+                                                const void* data,
+                                                size_t size) __INTRODUCED_IN(30);
+
+/**
+ *  Compress |pixels| as described by |info|.
+ *
+ *  @param info Description of the pixels to compress.
+ *  @param dataspace {@link ADataSpace} describing the color space of the
+ *                   pixels.
+ *  @param pixels Pointer to pixels to compress.
+ *  @param format {@link AndroidBitmapCompressFormat} to compress to.
+ *  @param quality Hint to the compressor, 0-100. The value is interpreted
+ *                 differently depending on the
+ *                 {@link AndroidBitmapCompressFormat}.
+ *  @param userContext User-defined data which will be passed to the supplied
+ *                     {@link AndroidBitmap_CompressWriteFunc} each time it is
+ *                     called. May be null.
+ *  @param fn Function that writes the compressed data. Will be called each time
+ *            the compressor has compressed more data that is ready to be
+ *            written. May be called more than once for each call to this method.
+ *            May not be null.
+ *  @return AndroidBitmap functions result code.
+ */
+int AndroidBitmap_compress(const AndroidBitmapInfo* info,
+                           int32_t dataspace,
+                           const void* pixels,
+                           int32_t format, int32_t quality,
+                           void* userContext,
+                           AndroidBitmap_CompressWriteFunc fn) __INTRODUCED_IN(30);
+
+struct AHardwareBuffer;
+
+/**
+ *  Retrieve the native object associated with a HARDWARE Bitmap.
+ *
+ *  Client must not modify it while a Bitmap is wrapping it.
+ *
+ *  @param bitmap Handle to an android.graphics.Bitmap.
+ *  @param outBuffer On success, is set to a pointer to the
+ *         {@link AHardwareBuffer} associated with bitmap. This acquires
+ *         a reference on the buffer, and the client must call
+ *         {@link AHardwareBuffer_release} when finished with it.
+ *  @return AndroidBitmap functions result code.
+ *          {@link ANDROID_BITMAP_RESULT_BAD_PARAMETER} if bitmap is not a
+ *          HARDWARE Bitmap.
+ */
+int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject bitmap,
+        AHardwareBuffer** outBuffer) __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h
new file mode 100644
index 0000000..3a87da0
--- /dev/null
+++ b/include/android/imagedecoder.h
@@ -0,0 +1,500 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup ImageDecoder
+ *
+ * Functions for converting encoded images into RGBA pixels.
+ *
+ * Similar to the Java counterpart android.graphics.ImageDecoder, it can be used
+ * to decode images in the following formats:
+ * - JPEG
+ * - PNG
+ * - GIF
+ * - WebP
+ * - BMP
+ * - ICO
+ * - WBMP
+ * - HEIF
+ * - Digital negatives (via the DNG SDK)
+ * <p>It has similar options for scaling, cropping, and choosing the output format.
+ * Unlike the Java API, which can create an android.graphics.Bitmap or
+ * android.graphics.drawable.Drawable object, AImageDecoder decodes directly
+ * into memory provided by the client. For more information, see the
+ * <a href="https://developer.android.com/ndk/guides/image-decoder">Image decoder</a>
+ * developer guide.
+ * @{
+ */
+
+/**
+ * @file imagedecoder.h
+ * @brief API for decoding images.
+ */
+
+#ifndef ANDROID_IMAGE_DECODER_H
+#define ANDROID_IMAGE_DECODER_H
+
+#include "bitmap.h"
+#include <android/rect.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AAsset;
+
+#if __ANDROID_API__ >= 30
+
+/**
+ *  {@link AImageDecoder} functions result code. Many functions will return one of these
+ *  to indicate success ({@link ANDROID_IMAGE_DECODER_SUCCESS}) or the reason
+ *  for the failure. On failure, any out-parameters should be considered
+ *  uninitialized, except where specified.
+ */
+enum {
+    /**
+     * Decoding was successful and complete.
+     */
+    ANDROID_IMAGE_DECODER_SUCCESS = 0,
+    /**
+     * The input is incomplete.
+     */
+    ANDROID_IMAGE_DECODER_INCOMPLETE = -1,
+    /**
+     * The input contained an error after decoding some lines.
+     */
+    ANDROID_IMAGE_DECODER_ERROR = -2,
+    /**
+     * Could not convert. For example, attempting to decode an image with
+     * alpha to an opaque format.
+     */
+    ANDROID_IMAGE_DECODER_INVALID_CONVERSION = -3,
+    /**
+     * The scale is invalid. It may have overflowed, or it may be incompatible
+     * with the current alpha setting.
+     */
+    ANDROID_IMAGE_DECODER_INVALID_SCALE = -4,
+    /**
+     * Some other parameter is invalid.
+     */
+    ANDROID_IMAGE_DECODER_BAD_PARAMETER = -5,
+    /**
+     * Input was invalid before decoding any pixels.
+     */
+    ANDROID_IMAGE_DECODER_INVALID_INPUT = -6,
+    /**
+     * A seek was required and it failed.
+     */
+    ANDROID_IMAGE_DECODER_SEEK_ERROR = -7,
+    /**
+     * Some other error. For example, an internal allocation failed.
+     */
+    ANDROID_IMAGE_DECODER_INTERNAL_ERROR = -8,
+    /**
+     * AImageDecoder did not recognize the format.
+     */
+    ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT = -9
+};
+
+struct AImageDecoder;
+
+/**
+ * Opaque handle for decoding images.
+ *
+ * Create using one of the following:
+ * - {@link AImageDecoder_createFromAAsset}
+ * - {@link AImageDecoder_createFromFd}
+ * - {@link AImageDecoder_createFromBuffer}
+ *
+ * After creation, {@link AImageDecoder_getHeaderInfo} can be used to retrieve
+ * information about the encoded image. Other functions, like
+ * {@link AImageDecoder_setTargetSize}, can be used to specify how to decode, and
+ * {@link AImageDecoder_decode} will decode into client provided memory.
+ *
+ * {@link AImageDecoder} objects are NOT thread-safe, and should not be shared across
+ * threads.
+ */
+typedef struct AImageDecoder AImageDecoder;
+
+/**
+ * Create a new {@link AImageDecoder} from an {@link AAsset}.
+ *
+ * @param asset {@link AAsset} containing encoded image data. Client is still
+ *              responsible for calling {@link AAsset_close} on it, which may be
+ *              done after deleting the returned {@link AImageDecoder}.
+ * @param outDecoder On success (i.e. return value is
+ *                   {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
+ *                   a newly created {@link AImageDecoder}. Caller is
+ *                   responsible for calling {@link AImageDecoder_delete} on it.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The asset was truncated before
+ *   reading the image header.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is
+ *   null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the
+ *   header.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset failed to seek.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ *   failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
+ *   supported.
+ */
+int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder)
+        __INTRODUCED_IN(30);
+
+/**
+ * Create a new {@link AImageDecoder} from a file descriptor.
+ *
+ * @param fd Seekable, readable, open file descriptor for encoded data.
+ *           Client is still responsible for closing it, which may be done
+ *           after deleting the returned {@link AImageDecoder}.
+ * @param outDecoder On success (i.e. return value is
+ *                   {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
+ *                   a newly created {@link AImageDecoder}. Caller is
+ *                   responsible for calling {@link AImageDecoder_delete} on it.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The file was truncated before
+ *   reading the image header.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The {@link AImageDecoder} is
+ *   null, or |fd| does not represent a valid, seekable file descriptor.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the
+ *   header.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The descriptor failed to seek.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ *   failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
+ *   supported.
+ */
+int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+
+/**
+ * Create a new AImageDecoder from a buffer.
+ *
+ * @param buffer Pointer to encoded data. Must be valid for the entire time
+ *               the {@link AImageDecoder} is used.
+ * @param length Byte length of buffer.
+ * @param outDecoder On success (i.e. return value is
+ *                   {@link ANDROID_IMAGE_DECODER_SUCCESS}), this will be set to
+ *                   a newly created {@link AImageDecoder}. Caller is
+ *                   responsible for calling {@link AImageDecoder_delete} on it.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The encoded image was truncated before
+ *   reading the image header.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: One of the parameters is
+ *   invalid.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_INPUT}: There is an error in the
+ *   header.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ *   failure to allocate memory.
+ * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not
+ *   supported.
+ */
+int AImageDecoder_createFromBuffer(const void* buffer, size_t length,
+                                   AImageDecoder** outDecoder) __INTRODUCED_IN(30);
+
+/**
+ * Delete the AImageDecoder.
+ */
+void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30);
+
+/**
+ * Choose the desired output format.
+ *
+ * @param format {@link AndroidBitmapFormat} to use for the output.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure. On failure, the
+ *         {@link AImageDecoder} uses the format it was already planning
+ *         to use (either its default or a previously successful setting
+ *         from this function).
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ *   {@link AImageDecoder} is null or |format| does not correspond to an
+ *   {@link AndroidBitmapFormat}.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The
+ *   {@link AndroidBitmapFormat} is incompatible with the image.
+ */
+int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*,
+        int32_t format) __INTRODUCED_IN(30);
+
+/**
+ * Specify whether the output's pixels should be unpremultiplied.
+ *
+ * By default, {@link AImageDecoder_decodeImage} will premultiply the pixels, if they have alpha.
+ * Pass true to this method to leave them unpremultiplied. This has no effect on an
+ * opaque image.
+ *
+ * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: Unpremultiplied is not
+ *   possible due to an existing scale set by
+ *   {@link AImageDecoder_setTargetSize}.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ *   {@link AImageDecoder} is null.
+ */
+int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*,
+                                             bool unpremultipliedRequired) __INTRODUCED_IN(30);
+
+/**
+ * Choose the dataspace for the output.
+ *
+ * Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support
+ * an {@link ADataSpace}.
+ *
+ * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace
+ *                  specifies how to interpret the colors. By default,
+ *                  AImageDecoder will decode into the ADataSpace specified by
+ *                  {@link AImageDecoderHeaderInfo_getDataSpace}. If this
+ *                  parameter is set to a different ADataSpace, AImageDecoder
+ *                  will transform the output into the specified ADataSpace.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ *   {@link AImageDecoder} is null or |dataspace| does not correspond to an
+ *   {@link ADataSpace} value.
+ */
+int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30);
+
+/**
+ * Specify the output size for a decoded image.
+ *
+ * Future calls to {@link AImageDecoder_decodeImage} will sample or scale the
+ * encoded image to reach the desired size. If a crop rect is set (via
+ * {@link AImageDecoder_setCrop}), it must be contained within the dimensions
+ * specified by width and height, and the output image will be the size of the
+ * crop rect.
+ *
+ * @param width Width of the output (prior to cropping).
+ *              This will affect future calls to
+ *              {@link AImageDecoder_getMinimumStride}, which will now return
+ *              a value based on this width.
+ * @param height Height of the output (prior to cropping).
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ *   {@link AImageDecoder} is null.
+ * - {@link ANDROID_IMAGE_DECODER_INVALID_SCALE}: |width| or |height| is <= 0,
+ *   the size is too big, any existing crop is not contained by the new image dimensions,
+ *   or the scale is incompatible with a previous call to
+ *   {@link AImageDecoder_setUnpremultipliedRequired}(true).
+ */
+int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30);
+
+
+/**
+ * Compute the dimensions to use for a given sampleSize.
+ *
+ * Although AImageDecoder can scale to an arbitrary target size (see
+ * {@link AImageDecoder_setTargetSize}), some sizes may be more efficient than
+ * others. This computes the most efficient target size to use to reach a
+ * particular sampleSize.
+ *
+ * @param sampleSize A subsampling rate of the original image. Must be greater
+ *                   than or equal to 1. A sampleSize of 2 means to skip every
+ *                   other pixel/line, resulting in a width and height that are
+ *                   1/2 of the original dimensions, with 1/4 the number of
+ *                   pixels.
+ * @param width Out parameter for the width sampled by sampleSize, and rounded
+ *              in the direction that the decoder can do most efficiently.
+ * @param height Out parameter for the height sampled by sampleSize, and rounded
+ *               in the direction that the decoder can do most efficiently.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ *   {@link AImageDecoder}, |width| or |height| is null or |sampleSize| is < 1.
+ */
+int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize,
+                                     int32_t* width, int32_t* height) __INTRODUCED_IN(30);
+/**
+ * Specify how to crop the output after scaling (if any).
+ *
+ * Future calls to {@link AImageDecoder_decodeImage} will crop their output to
+ * the specified {@link ARect}. Clients will only need to allocate enough memory
+ * for the cropped ARect.
+ *
+ * @param crop Rectangle describing a crop of the decode. It must be contained inside of
+ *             the (possibly scaled, by {@link AImageDecoder_setTargetSize})
+ *             image dimensions. This will affect future calls to
+ *             {@link AImageDecoder_getMinimumStride}, which will now return a
+ *             value based on the width of the crop. An empty ARect -
+ *             specifically { 0, 0, 0, 0 } - may be used to remove the cropping
+ *             behavior. Any other empty or unsorted ARects will result in
+ *             returning {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The
+ *   {@link AImageDecoder} is null or the crop is not contained by the
+ *   (possibly scaled) image dimensions.
+ */
+int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30);
+
+struct AImageDecoderHeaderInfo;
+/**
+ * Opaque handle for representing information about the encoded image. Retrieved
+ * using {@link AImageDecoder_getHeaderInfo} and passed to methods like
+ * {@link AImageDecoderHeaderInfo_getWidth} and
+ * {@link AImageDecoderHeaderInfo_getHeight}.
+ */
+typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo;
+
+/**
+ * Return an opaque handle for reading header info.
+ *
+ * This is owned by the {@link AImageDecoder} and will be destroyed when the
+ * AImageDecoder is destroyed via {@link AImageDecoder_delete}.
+ */
+const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(
+        const AImageDecoder*) __INTRODUCED_IN(30);
+
+/**
+ * Report the native width of the encoded image. This is also the logical
+ * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is
+ * used to choose a different size or {@link AImageDecoder_setCrop} is used to
+ * set a crop rect.
+ */
+int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report the native height of the encoded image. This is also the logical
+ * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is
+ * used to choose a different size or {@link AImageDecoder_setCrop} is used to
+ * set a crop rect.
+ */
+int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report the mimeType of the encoded image.
+ *
+ * @return a string literal describing the mime type.
+ */
+const char* AImageDecoderHeaderInfo_getMimeType(
+        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to
+ * by default. {@link AImageDecoder} will try to choose one that is sensible
+ * for the image and the system. Note that this does not indicate the
+ * encoded format of the image.
+ */
+int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(
+        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report how the {@link AImageDecoder} will handle alpha by default. If the image
+ * contains no alpha (according to its header), this will return
+ * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha,
+ * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because
+ * {@link AImageDecoder_decodeImage} will premultiply pixels by default.
+ */
+int AImageDecoderHeaderInfo_getAlphaFlags(
+        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Report the dataspace the AImageDecoder will decode to by default.
+ *
+ * By default, {@link AImageDecoder_decodeImage} will not do any color
+ * conversion.
+ *
+ * @return The {@link ADataSpace} representing the way the colors
+ *         are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a
+ *         corresponding ADataSpace). This specifies how to interpret the colors
+ *         in the decoded image, unless {@link AImageDecoder_setDataSpace} is
+ *         called to decode to a different ADataSpace.
+ *
+ *         Note that ADataSpace only exposes a few values. This may return
+ *         {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have
+ *         no corresponding {@link ADataSpace}.
+ */
+int32_t AImageDecoderHeaderInfo_getDataSpace(
+        const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30);
+
+/**
+ * Return the minimum stride that can be used in
+ * {@link AImageDecoder_decodeImage).
+ *
+ * This stride provides no padding, meaning it will be exactly equal to the
+ * width times the number of bytes per pixel for the {@link AndroidBitmapFormat}
+ * being used.
+ *
+ * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or
+ * cropped (via {@link AImageDecoder_setCrop}), this takes those into account.
+ */
+size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30);
+
+/**
+ * Decode the image into pixels, using the settings of the {@link AImageDecoder}.
+ *
+ * @param decoder Opaque object representing the decoder.
+ * @param pixels On success, will be filled with the result
+ *               of the decode. Must be large enough to hold |size| bytes.
+ * @param stride Width in bytes of a single row. Must be at least
+ *               {@link AImageDecoder_getMinimumStride} and a multiple of the
+ *               bytes per pixel of the {@link AndroidBitmapFormat}.
+ * @param size Size of the pixel buffer in bytes. Must be at least
+ *             stride * (height - 1) +
+ *             {@link AImageDecoder_getMinimumStride}.
+ * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value
+ *         indicating the reason for the failure.
+ *
+ * Errors:
+ * - {@link ANDROID_IMAGE_DECODER_INCOMPLETE}: The image was truncated. A
+ *   partial image was decoded, and undecoded lines have been initialized to all
+ *   zeroes.
+ * - {@link ANDROID_IMAGE_DECODER_ERROR}: The image contained an error. A
+ *   partial image was decoded, and undecoded lines have been initialized to all
+ *   zeroes.
+ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The {@link AImageDecoder} or
+ *   |pixels| is null, the stride is not large enough or not pixel aligned, or
+ *   |size| is not large enough.
+ * - {@link ANDROID_IMAGE_DECODER_SEEK_ERROR}: The asset or file descriptor
+ *   failed to seek.
+ * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a
+ *   failure to allocate memory.
+ */
+int AImageDecoder_decodeImage(AImageDecoder* decoder,
+                              void* pixels, size_t stride,
+                              size_t size) __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_IMAGE_DECODER_H
+
+/** @} */
diff --git a/include/android/input.h b/include/android/input.h
index ce439c6..dbfd61e 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -158,7 +158,10 @@
     AINPUT_EVENT_TYPE_KEY = 1,
 
     /** Indicates that the input event is a motion event. */
-    AINPUT_EVENT_TYPE_MOTION = 2
+    AINPUT_EVENT_TYPE_MOTION = 2,
+
+    /** Focus event */
+    AINPUT_EVENT_TYPE_FOCUS = 3,
 };
 
 /**
@@ -790,6 +793,8 @@
     AMOTION_EVENT_TOOL_TYPE_MOUSE = 3,
     /** eraser */
     AMOTION_EVENT_TOOL_TYPE_ERASER = 4,
+    /** palm */
+    AMOTION_EVENT_TOOL_TYPE_PALM = 5,
 };
 
 /**
diff --git a/include/android/sensor.h b/include/android/sensor.h
index 3ebe79f..e63ac4b 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -15,6 +15,9 @@
  */
 
 /**
+ * Structures and functions to receive and process sensor events in
+ * native code.
+ *
  * @addtogroup Sensor
  * @{
  */
@@ -42,12 +45,6 @@
  *   - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
  */
 
-/**
- * Structures and functions to receive and process sensor events in
- * native code.
- *
- */
-
 #include <android/looper.h>
 
 #include <stdbool.h>
@@ -245,6 +242,10 @@
      * {@link ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED}
      */
     ASENSOR_TYPE_ACCELEROMETER_UNCALIBRATED = 35,
+    /**
+     * {@link ASENSOR_TYPE_HINGE_ANGLE}
+     */
+    ASENSOR_TYPE_HINGE_ANGLE = 36,
 };
 
 /**
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 90e5653..eeb8330 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -149,7 +149,7 @@
 /**
  * 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.
+ * it. If a device does not support present fences, a -1 will be returned.
  *
  * Available since API level 29.
  */
@@ -407,6 +407,33 @@
 
 #endif // __ANDROID_API__ >= 29
 
+#if __ANDROID_API__ >= 30
+
+/*
+ * Sets the intended frame rate for |surface_control|.
+ *
+ * On devices that are capable of running the display at different refresh rates, the system may
+ * choose a display refresh rate to better match this surface's frame rate. Usage of this API won't
+ * directly affect the application's frame production pipeline. However, because the system may
+ * change the display refresh rate, calls to this function may result in changes to Choreographer
+ * callback timings, and changes to the time interval at which the system releases buffers back to
+ * the application.
+ *
+ * |frameRate| is the intended frame rate of this surface, in frames per second. 0 is a special
+ * value that indicates the app will accept the system's choice for the display frame rate, which is
+ * the default behavior if this function isn't called. The frameRate param does *not* need to be a
+ * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can
+ * only run the display at 60fps.
+ *
+ * Available since API level 30.
+ */
+void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
+                                      ASurfaceControl* surface_control,
+                                      float frameRate)
+                                      __INTRODUCED_IN(30);
+
+#endif // __ANDROID_API__ >= 30
+
 __END_DECLS
 
 #endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index fa456bb..6100626 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -17,16 +17,23 @@
 #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 <cinttypes>
 #include <optional>
 
+#include <android-base/stringprintf.h>
+#include <input/Input.h>
+
 using android::base::StringPrintf;
 
 namespace android {
 
+enum {
+    DISPLAY_ORIENTATION_0 = 0,
+    DISPLAY_ORIENTATION_90 = 1,
+    DISPLAY_ORIENTATION_180 = 2,
+    DISPLAY_ORIENTATION_270 = 3
+};
+
 /**
  * Describes the different type of viewports supported by input flinger.
  * Keep in sync with values in InputManagerService.java.
diff --git a/include/input/Input.h b/include/input/Input.h
index 805957a..14a7288 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -24,12 +24,16 @@
  */
 
 #include <android/input.h>
+#include <math.h>
+#include <stdint.h>
 #include <utils/BitSet.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
 #include <utils/Timers.h>
 #include <utils/Vector.h>
-#include <stdint.h>
+#include <array>
+#include <limits>
+#include <queue>
 
 /*
  * Additional private constants not defined in ndk/ui/input.h.
@@ -69,6 +73,19 @@
     AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
 };
 
+/**
+ * Allowed VerifiedKeyEvent flags. All other flags from KeyEvent do not get verified.
+ * These values must be kept in sync with VerifiedKeyEvent.java
+ */
+constexpr int32_t VERIFIED_KEY_EVENT_FLAGS = AKEY_EVENT_FLAG_CANCELED;
+
+/**
+ * Allowed VerifiedMotionEventFlags. All other flags from MotionEvent do not get verified.
+ * These values must be kept in sync with VerifiedMotionEvent.java
+ */
+constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS =
+        AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
 enum {
     /* Used when a motion event is not associated with any display.
      * Typically used for non-pointer events. */
@@ -164,6 +181,8 @@
 class Parcel;
 #endif
 
+const char* inputEventTypeToString(int32_t type);
+
 /*
  * Flags that flow alongside events in the input dispatch system to help with certain
  * policy decisions such as waking from device sleep.
@@ -246,6 +265,18 @@
  */
 const char* motionClassificationToString(MotionClassification classification);
 
+/**
+ * Invalid value for cursor position. Used for non-mouse events, tests and injected events. Don't
+ * use it for direct comparison with any other value, because NaN isn't equal to itself according to
+ * IEEE 754. Use isnan() instead to check if a cursor position is valid.
+ */
+constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN();
+
+/**
+ * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified.
+ */
+constexpr std::array<uint8_t, 32> INVALID_HMAC = {0};
+
 /*
  * Pointer coordinate data.
  */
@@ -336,22 +367,25 @@
 
     inline int32_t getDeviceId() const { return mDeviceId; }
 
-    inline int32_t getSource() const { return mSource; }
+    inline uint32_t getSource() const { return mSource; }
 
-    inline void setSource(int32_t source) { mSource = source; }
+    inline void setSource(uint32_t source) { mSource = source; }
 
     inline int32_t getDisplayId() const { return mDisplayId; }
 
     inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
 
+    inline std::array<uint8_t, 32> getHmac() const { return mHmac; }
 
 protected:
-    void initialize(int32_t deviceId, int32_t source, int32_t displayId);
+    void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                    std::array<uint8_t, 32> hmac);
     void initialize(const InputEvent& from);
 
     int32_t mDeviceId;
-    int32_t mSource;
+    uint32_t mSource;
     int32_t mDisplayId;
+    std::array<uint8_t, 32> mHmac;
 };
 
 /*
@@ -384,18 +418,10 @@
     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,
-            int32_t scanCode,
-            int32_t metaState,
-            int32_t repeatCount,
-            nsecs_t downTime,
-            nsecs_t eventTime);
+    void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                    std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
+                    int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+                    nsecs_t eventTime);
     void initialize(const KeyEvent& from);
 
 protected:
@@ -451,6 +477,10 @@
 
     inline void setActionButton(int32_t button) { mActionButton = button; }
 
+    inline float getXScale() const { return mXScale; }
+
+    inline float getYScale() const { return mYScale; }
+
     inline float getXOffset() const { return mXOffset; }
 
     inline float getYOffset() const { return mYOffset; }
@@ -459,6 +489,18 @@
 
     inline float getYPrecision() const { return mYPrecision; }
 
+    inline float getRawXCursorPosition() const { return mRawXCursorPosition; }
+
+    float getXCursorPosition() const;
+
+    inline float getRawYCursorPosition() const { return mRawYCursorPosition; }
+
+    float getYCursorPosition() const;
+
+    void setCursorPosition(float x, float y);
+
+    static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); }
+
     inline nsecs_t getDownTime() const { return mDownTime; }
 
     inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; }
@@ -600,26 +642,14 @@
 
     ssize_t findPointerIndex(int32_t pointerId) const;
 
-    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,
-            float yPrecision,
-            nsecs_t downTime,
-            nsecs_t eventTime,
-            size_t pointerCount,
-            const PointerProperties* pointerProperties,
-            const PointerCoords* pointerCoords);
+    void initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                    std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+                    int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+                    MotionClassification classification, float xScale, float yScale, float xOffset,
+                    float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition,
+                    float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+                    size_t pointerCount, const PointerProperties* pointerProperties,
+                    const PointerCoords* pointerCoords);
 
     void copyFrom(const MotionEvent* other, bool keepHistory);
 
@@ -640,7 +670,7 @@
     status_t writeToParcel(Parcel* parcel) const;
 #endif
 
-    static bool isTouchEvent(int32_t source, int32_t action);
+    static bool isTouchEvent(uint32_t source, int32_t action);
     inline bool isTouchEvent() const {
         return isTouchEvent(mSource, mAction);
     }
@@ -665,10 +695,14 @@
     int32_t mMetaState;
     int32_t mButtonState;
     MotionClassification mClassification;
+    float mXScale;
+    float mYScale;
     float mXOffset;
     float mYOffset;
     float mXPrecision;
     float mYPrecision;
+    float mRawXCursorPosition;
+    float mRawYCursorPosition;
     nsecs_t mDownTime;
     Vector<PointerProperties> mPointerProperties;
     Vector<nsecs_t> mSampleEventTimes;
@@ -676,6 +710,77 @@
 };
 
 /*
+ * Focus events.
+ */
+class FocusEvent : public InputEvent {
+public:
+    virtual ~FocusEvent() {}
+
+    virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; }
+
+    inline bool getHasFocus() const { return mHasFocus; }
+
+    inline bool getInTouchMode() const { return mInTouchMode; }
+
+    void initialize(bool hasFocus, bool inTouchMode);
+
+    void initialize(const FocusEvent& from);
+
+protected:
+    bool mHasFocus;
+    bool mInTouchMode;
+};
+
+/**
+ * Base class for verified events.
+ * Do not create a VerifiedInputEvent explicitly.
+ * Use helper functions to create them from InputEvents.
+ */
+struct __attribute__((__packed__)) VerifiedInputEvent {
+    enum class Type : int32_t {
+        KEY = AINPUT_EVENT_TYPE_KEY,
+        MOTION = AINPUT_EVENT_TYPE_MOTION,
+    };
+
+    Type type;
+    int32_t deviceId;
+    nsecs_t eventTimeNanos;
+    uint32_t source;
+    int32_t displayId;
+};
+
+/**
+ * Same as KeyEvent, but only contains the data that can be verified.
+ * If you update this class, you must also update VerifiedKeyEvent.java
+ */
+struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent {
+    int32_t action;
+    nsecs_t downTimeNanos;
+    int32_t flags;
+    int32_t keyCode;
+    int32_t scanCode;
+    int32_t metaState;
+    int32_t repeatCount;
+};
+
+/**
+ * Same as MotionEvent, but only contains the data that can be verified.
+ * If you update this class, you must also update VerifiedMotionEvent.java
+ */
+struct __attribute__((__packed__)) VerifiedMotionEvent : public VerifiedInputEvent {
+    float rawX;
+    float rawY;
+    int32_t actionMasked;
+    nsecs_t downTimeNanos;
+    int32_t flags;
+    int32_t metaState;
+    int32_t buttonState;
+};
+
+VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event);
+VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event);
+
+/*
  * Input event factory.
  */
 class InputEventFactoryInterface {
@@ -687,6 +792,7 @@
 
     virtual KeyEvent* createKeyEvent() = 0;
     virtual MotionEvent* createMotionEvent() = 0;
+    virtual FocusEvent* createFocusEvent() = 0;
 };
 
 /*
@@ -698,12 +804,14 @@
     PreallocatedInputEventFactory() { }
     virtual ~PreallocatedInputEventFactory() { }
 
-    virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
-    virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
+    virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
+    virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
+    virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
 
 private:
     KeyEvent mKeyEvent;
     MotionEvent mMotionEvent;
+    FocusEvent mFocusEvent;
 };
 
 /*
@@ -714,16 +822,18 @@
     explicit PooledInputEventFactory(size_t maxPoolSize = 20);
     virtual ~PooledInputEventFactory();
 
-    virtual KeyEvent* createKeyEvent();
-    virtual MotionEvent* createMotionEvent();
+    virtual KeyEvent* createKeyEvent() override;
+    virtual MotionEvent* createMotionEvent() override;
+    virtual FocusEvent* createFocusEvent() override;
 
     void recycle(InputEvent* event);
 
 private:
     const size_t mMaxPoolSize;
 
-    Vector<KeyEvent*> mKeyEventPool;
-    Vector<MotionEvent*> mMotionEventPool;
+    std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
+    std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
+    std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
 };
 
 } // namespace android
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index 59d16d1..b327d76 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -359,6 +359,9 @@
     DEFINE_AXIS(BRAKE),
     DEFINE_AXIS(DISTANCE),
     DEFINE_AXIS(TILT),
+    DEFINE_AXIS(SCROLL),
+    DEFINE_AXIS(RELATIVE_X),
+    DEFINE_AXIS(RELATIVE_Y),
     DEFINE_AXIS(GENERIC_1),
     DEFINE_AXIS(GENERIC_2),
     DEFINE_AXIS(GENERIC_3),
@@ -402,13 +405,12 @@
     { nullptr, 0 }
 };
 
-static const InputEventLabel FLAGS[] = {
-    DEFINE_FLAG(VIRTUAL),
-    DEFINE_FLAG(FUNCTION),
-    DEFINE_FLAG(GESTURE),
+static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
+                                        DEFINE_FLAG(FUNCTION),
+                                        DEFINE_FLAG(GESTURE),
+                                        DEFINE_FLAG(WAKE),
 
-    { nullptr, 0 }
-};
+                                        {nullptr, 0}};
 
 static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
     while (list->literal) {
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 63606e5..06fd3bb 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -31,13 +31,17 @@
 
 #include <string>
 
+#include <android-base/chrono_utils.h>
+
 #include <binder/IBinder.h>
 #include <input/Input.h>
-#include <utils/Errors.h>
-#include <utils/Timers.h>
-#include <utils/RefBase.h>
-#include <utils/Vector.h>
 #include <utils/BitSet.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+#include <utils/Vector.h>
+
+#include <android-base/unique_fd.h>
 
 namespace android {
 class Parcel;
@@ -56,14 +60,15 @@
  * in StructLayout_test should be made.
  */
 struct InputMessage {
-    enum {
-        TYPE_KEY = 1,
-        TYPE_MOTION = 2,
-        TYPE_FINISHED = 3,
+    enum class Type : uint32_t {
+        KEY,
+        MOTION,
+        FINISHED,
+        FOCUS,
     };
 
     struct Header {
-        uint32_t type;
+        Type type; // 4 bytes
         // We don't need this field in order to align the body below but we
         // leave it here because InputMessage::size() and other functions
         // compute the size of this structure as sizeof(Header) + sizeof(Body).
@@ -71,6 +76,9 @@
     } header;
 
     // Body *must* be 8 byte aligned.
+    // For keys and motions, rely on the fact that std::array takes up exactly as much space
+    // as the underlying data. This is not guaranteed by C++, but it simplifies the conversions.
+    static_assert(sizeof(std::array<uint8_t, 32>) == 32);
     union Body {
         struct Key {
             uint32_t seq;
@@ -79,6 +87,7 @@
             int32_t deviceId;
             int32_t source;
             int32_t displayId;
+            std::array<uint8_t, 32> hmac;
             int32_t action;
             int32_t flags;
             int32_t keyCode;
@@ -88,9 +97,7 @@
             uint32_t empty2;
             nsecs_t downTime __attribute__((aligned(8)));
 
-            inline size_t size() const {
-                return sizeof(Key);
-            }
+            inline size_t size() const { return sizeof(Key); }
         } key;
 
         struct Motion {
@@ -100,26 +107,36 @@
             int32_t deviceId;
             int32_t source;
             int32_t displayId;
+            std::array<uint8_t, 32> hmac;
             int32_t action;
             int32_t actionButton;
             int32_t flags;
             int32_t metaState;
             int32_t buttonState;
             MotionClassification classification; // base type: uint8_t
-            uint8_t empty2[3];
+            uint8_t empty2[3];                   // 3 bytes to fill gap created by classification
             int32_t edgeFlags;
             nsecs_t downTime __attribute__((aligned(8)));
+            float xScale;
+            float yScale;
             float xOffset;
             float yOffset;
             float xPrecision;
             float yPrecision;
+            float xCursorPosition;
+            float yCursorPosition;
             uint32_t pointerCount;
             uint32_t empty3;
-            // Note that PointerCoords requires 8 byte alignment.
+            /**
+             * The "pointers" field must be the last field of the struct InputMessage.
+             * When we send the struct InputMessage across the socket, we are not
+             * writing the entire "pointers" array, but only the pointerCount portion
+             * of it as an optimization. Adding a field after "pointers" would break this.
+             */
             struct Pointer {
                 PointerProperties properties;
                 PointerCoords coords;
-            } pointers[MAX_POINTERS];
+            } pointers[MAX_POINTERS] __attribute__((aligned(8)));
 
             int32_t getActionId() const {
                 uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
@@ -135,12 +152,19 @@
 
         struct Finished {
             uint32_t seq;
-            bool handled;
+            uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
 
-            inline size_t size() const {
-                return sizeof(Finished);
-            }
+            inline size_t size() const { return sizeof(Finished); }
         } finished;
+
+        struct Focus {
+            uint32_t seq;
+            // The following two fields take up 4 bytes total
+            uint16_t hasFocus;    // actually a bool
+            uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
+
+            inline size_t size() const { return sizeof(Focus); }
+        } focus;
     } __attribute__((aligned(8))) body;
 
     bool isValid(size_t actualSize) const;
@@ -161,60 +185,73 @@
     virtual ~InputChannel();
 
 public:
-    InputChannel() = default;
-    InputChannel(const std::string& name, int fd);
+    static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd,
+                                   sp<IBinder> token);
 
-    /* Creates a pair of input channels.
+    /**
+     * Create a pair of input channels.
+     * The two returned input channels are equivalent, and are labeled as "server" and "client"
+     * for convenience. The two input channels share the same token.
      *
-     * Returns OK on success.
+     * Return OK on success.
      */
     static status_t openInputChannelPair(const std::string& name,
             sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel);
 
     inline std::string getName() const { return mName; }
-    inline int getFd() const { return mFd; }
+    inline int getFd() const { return mFd.get(); }
 
-    /* Sends a message to the other endpoint.
+    /* Send a message to the other endpoint.
      *
      * If the channel is full then the message is guaranteed not to have been sent at all.
      * Try again after the consumer has sent a finished signal indicating that it has
      * consumed some of the pending messages from the channel.
      *
-     * Returns OK on success.
-     * Returns WOULD_BLOCK if the channel is full.
-     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Return OK on success.
+     * Return WOULD_BLOCK if the channel is full.
+     * Return DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
     status_t sendMessage(const InputMessage* msg);
 
-    /* Receives a message sent by the other endpoint.
+    /* Receive a message sent by the other endpoint.
      *
      * If there is no message present, try again after poll() indicates that the fd
      * is readable.
      *
-     * Returns OK on success.
-     * Returns WOULD_BLOCK if there is no message present.
-     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Return OK on success.
+     * Return WOULD_BLOCK if there is no message present.
+     * Return DEAD_OBJECT if the channel's peer has been closed.
      * Other errors probably indicate that the channel is broken.
      */
     status_t receiveMessage(InputMessage* msg);
 
-    /* Returns a new object that has a duplicate of this channel's fd. */
+    /* Return 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);
+    static sp<InputChannel> read(const Parcel& from);
 
-    sp<IBinder> getToken() const;
-    void setToken(const sp<IBinder>& token);
+    /**
+     * The connection token is used to identify the input connection, i.e.
+     * the pair of input channels that were created simultaneously. Input channels
+     * are always created in pairs, and the token can be used to find the server-side
+     * input channel from the client-side input channel, and vice versa.
+     *
+     * Do not use connection token to check equality of a specific input channel object
+     * to another, because two different (client and server) input channels will share the
+     * same connection token.
+     *
+     * Return the token that identifies this connection.
+     */
+    sp<IBinder> getConnectionToken() const;
 
 private:
-    void setFd(int fd);
-
+    InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token);
     std::string mName;
-    int mFd = -1;
+    android::base::unique_fd mFd;
 
-    sp<IBinder> mToken = nullptr;
+    sp<IBinder> mToken;
 };
 
 /*
@@ -239,19 +276,10 @@
      * Returns BAD_VALUE if seq is 0.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t publishKeyEvent(
-            uint32_t seq,
-            int32_t deviceId,
-            int32_t source,
-            int32_t displayId,
-            int32_t action,
-            int32_t flags,
-            int32_t keyCode,
-            int32_t scanCode,
-            int32_t metaState,
-            int32_t repeatCount,
-            nsecs_t downTime,
-            nsecs_t eventTime);
+    status_t publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+                             std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+                             int32_t keyCode, int32_t scanCode, int32_t metaState,
+                             int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime);
 
     /* Publishes a motion event to the input channel.
      *
@@ -261,27 +289,24 @@
      * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t publishMotionEvent(
-            uint32_t seq,
-            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,
-            float yPrecision,
-            nsecs_t downTime,
-            nsecs_t eventTime,
-            uint32_t pointerCount,
-            const PointerProperties* pointerProperties,
-            const PointerCoords* pointerCoords);
+    status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+                                std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+                                int32_t flags, int32_t edgeFlags, int32_t metaState,
+                                int32_t buttonState, MotionClassification classification,
+                                float xScale, float yScale, float xOffset, float yOffset,
+                                float xPrecision, float yPrecision, float xCursorPosition,
+                                float yCursorPosition, nsecs_t downTime, nsecs_t eventTime,
+                                uint32_t pointerCount, const PointerProperties* pointerProperties,
+                                const PointerCoords* pointerCoords);
+
+    /* Publishes a focus event to the input channel.
+     *
+     * Returns OK on success.
+     * Returns WOULD_BLOCK if the channel is full.
+     * Returns DEAD_OBJECT if the channel's peer has been closed.
+     * Other errors probably indicate that the channel is broken.
+     */
+    status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode);
 
     /* Receives the finished signal from the consumer in reply to the original dispatch signal.
      * If a signal was received, returns the message sequence number,
@@ -297,6 +322,7 @@
     status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled);
 
 private:
+
     sp<InputChannel> mChannel;
 };
 
@@ -337,8 +363,8 @@
      * Returns NO_MEMORY if the event could not be created.
      * Other errors probably indicate that the channel is broken.
      */
-    status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
-            nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
+    status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, 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
@@ -509,6 +535,7 @@
     static void rewriteMessage(TouchState& state, InputMessage& msg);
     static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
     static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
+    static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
     static void addSample(MotionEvent* event, const InputMessage* msg);
     static bool canAddSample(const Batch& batch, const InputMessage* msg);
     static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 916af69..c44db51 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -63,7 +63,6 @@
         FLAG_DISMISS_KEYGUARD = 0x00400000,
         FLAG_SPLIT_TOUCH = 0x00800000,
         FLAG_SLIPPERY = 0x20000000,
-        FLAG_NEEDS_MENU_KEY = 0x40000000,
     };
 
     // Window types from WindowManager.LayoutParams
@@ -120,7 +119,11 @@
     /* These values are filled in by the WM and passed through SurfaceFlinger
      * unless specified otherwise.
      */
+    // This value should NOT be used to uniquely identify the window. There may be different
+    // input windows that have the same token.
     sp<IBinder> token;
+    // This uniquely identifies the input window.
+    int32_t id = 0;
     std::string name;
     int32_t layoutParamsFlags;
     int32_t layoutParamsType;
@@ -158,7 +161,6 @@
     bool hasFocus;
     bool hasWallpaper;
     bool paused;
-    int32_t layer;
     int32_t ownerPid;
     int32_t ownerUid;
     int32_t inputFeatures;
@@ -204,6 +206,8 @@
 
     sp<IBinder> getToken() const;
 
+    int32_t getId() const { return mInfo.id; }
+
     sp<IBinder> getApplicationToken() {
         return mInfo.applicationInfo.token;
     }
diff --git a/include/input/LatencyStatistics.h b/include/input/LatencyStatistics.h
new file mode 100644
index 0000000..bd86266
--- /dev/null
+++ b/include/input/LatencyStatistics.h
@@ -0,0 +1,59 @@
+/*
+ * 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_STATISTICS_H
+#define _UI_INPUT_STATISTICS_H
+
+#include <android-base/chrono_utils.h>
+
+#include <stddef.h>
+
+namespace android {
+
+class LatencyStatistics {
+private:
+    /* Minimum sample recorded */
+    float mMin;
+    /* Maximum sample recorded */
+    float mMax;
+    /* Sum of all samples recorded */
+    float mSum;
+    /* Sum of all the squares of samples recorded */
+    float mSum2;
+    /* Count of all samples recorded */
+    size_t mCount;
+    /* The last time statistics were reported */
+    std::chrono::steady_clock::time_point mLastReportTime;
+    /* Statistics Report Frequency */
+    const std::chrono::seconds mReportPeriod;
+
+public:
+    LatencyStatistics(std::chrono::seconds period);
+
+    void addValue(float);
+    void reset();
+    bool shouldReport();
+
+    float getMean();
+    float getMin();
+    float getMax();
+    float getStDev();
+    size_t getCount();
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_STATISTICS_H
diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h
index b49c623..4fa2f86 100644
--- a/include/input/TouchVideoFrame.h
+++ b/include/input/TouchVideoFrame.h
@@ -19,7 +19,6 @@
 
 #include <stdint.h>
 #include <sys/time.h>
-#include <ui/DisplayInfo.h>
 #include <vector>
 
 namespace android {
diff --git a/include/ui b/include/ui
deleted file mode 120000
index 2fb3147..0000000
--- a/include/ui
+++ /dev/null
@@ -1 +0,0 @@
-../libs/ui/include/ui
\ No newline at end of file
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
new file mode 120000
index 0000000..9a195ea
--- /dev/null
+++ b/include/ui/DisplayInfo.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/DisplayInfo.h
\ No newline at end of file
diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h
new file mode 120000
index 0000000..c2047c0
--- /dev/null
+++ b/include/ui/FatVector.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/FatVector.h
\ No newline at end of file
diff --git a/include/ui/FloatRect.h b/include/ui/FloatRect.h
new file mode 120000
index 0000000..d7bd737
--- /dev/null
+++ b/include/ui/FloatRect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/FloatRect.h
\ No newline at end of file
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
new file mode 120000
index 0000000..4085433
--- /dev/null
+++ b/include/ui/PixelFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PixelFormat.h
\ No newline at end of file
diff --git a/include/ui/Point.h b/include/ui/Point.h
new file mode 120000
index 0000000..443938b
--- /dev/null
+++ b/include/ui/Point.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Point.h
\ No newline at end of file
diff --git a/include/ui/PublicFormat.h b/include/ui/PublicFormat.h
new file mode 120000
index 0000000..7984c0e
--- /dev/null
+++ b/include/ui/PublicFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PublicFormat.h
\ No newline at end of file
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
new file mode 120000
index 0000000..a99c5f2
--- /dev/null
+++ b/include/ui/Rect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rect.h
\ No newline at end of file
diff --git a/include/ui/Region.h b/include/ui/Region.h
new file mode 120000
index 0000000..2e46e0f
--- /dev/null
+++ b/include/ui/Region.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Region.h
\ No newline at end of file
diff --git a/include/ui/Size.h b/include/ui/Size.h
new file mode 120000
index 0000000..c0da99b
--- /dev/null
+++ b/include/ui/Size.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Size.h
\ No newline at end of file
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 0a6685e..aeca12b 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -21,6 +21,13 @@
 
 #include <utils/SystemClock.h>
 
+#include <sys/types.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "AppOpsManager"
+
 namespace android {
 
 namespace {
@@ -37,18 +44,24 @@
 }  // namespace
 
 static String16 _appops("appops");
-static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gToken;
+static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+static sp<IBinder> gClientId;
 
-static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) {
-    pthread_mutex_lock(&gTokenMutex);
-    if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) {
-        gToken = service->getToken(new BBinder());
+static const sp<IBinder>& getClientId() {
+    pthread_mutex_lock(&gClientIdMutex);
+    if (gClientId == nullptr) {
+        gClientId = new BBinder();
     }
-    pthread_mutex_unlock(&gTokenMutex);
-    return gToken;
+    pthread_mutex_unlock(&gClientIdMutex);
+    return gClientId;
 }
 
+thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
+thread_local int32_t uidOfThisBinderTransaction = -1;
+
+// Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note
+uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0};
+
 AppOpsManager::AppOpsManager()
 {
 }
@@ -102,24 +115,48 @@
 }
 
 int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
+    return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(),
+            String16("Legacy AppOpsManager.noteOp call"));
+}
+
+int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
+        const std::unique_ptr<String16>& featureId, const String16& message) {
     sp<IAppOpsService> service = getService();
-    return service != nullptr
-            ? service->noteOperation(op, uid, callingPackage)
+    int32_t mode = service != nullptr
+            ? service->noteOperation(op, uid, callingPackage, featureId, shouldCollectNotes(op),
+                    message)
             : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+
+    return mode;
 }
 
 int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
         bool startIfModeDefault) {
+    return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(),
+            String16("Legacy AppOpsManager.startOpNoThrow call"));
+}
+
+int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
+        bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+        const String16& message) {
     sp<IAppOpsService> service = getService();
-    return service != nullptr
-            ? service->startOperation(getToken(service), op, uid, callingPackage,
-                    startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+    int32_t mode = service != nullptr
+            ? service->startOperation(getClientId(), op, uid, callingPackage,
+                    featureId, startIfModeDefault, shouldCollectNotes(op), message)
+            : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+
+    return mode;
 }
 
 void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) {
+    finishOp(op, uid, callingPackage, std::unique_ptr<String16>());
+}
+
+void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
+        const std::unique_ptr<String16>& callingFeatureId) {
     sp<IAppOpsService> service = getService();
     if (service != nullptr) {
-        service->finishOperation(getToken(service), op, uid, callingPackage);
+        service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
     }
 }
 
@@ -146,5 +183,24 @@
     return -1;
 }
 
+void AppOpsManager::setCameraAudioRestriction(int32_t mode) {
+    sp<IAppOpsService> service = getService();
+    if (service != nullptr) {
+        service->setCameraAudioRestriction(mode);
+    }
+}
+
+// check it the appops needs to be collected and cache result
+bool AppOpsManager::shouldCollectNotes(int32_t opcode) {
+    if (appOpsToNote[opcode] == 0) {
+        if (getService()->shouldCollectNotes(opcode)) {
+            appOpsToNote[opcode] = 2;
+        } else {
+            appOpsToNote[opcode] = 1;
+        }
+    }
+
+    return appOpsToNote[opcode] == 2;
+}
 
 } // namespace android
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index 856a178..8cf6097 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -49,9 +49,10 @@
     }
     
     status_t append(const char* txt, size_t len) {
+        if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
         if ((len+bufferPos) > bufferSize) {
+            if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
             size_t newSize = ((len+bufferPos)*3)/2;
-            if (newSize < (len+bufferPos)) return NO_MEMORY;    // overflow
             void* b = realloc(buffer, newSize);
             if (!b) return NO_MEMORY;
             buffer = (char*)b;
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index b2bd9e5..a5555a3 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -46,12 +46,17 @@
         return reply.readInt32();
     }
 
-    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) {
+    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+                const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+                const String16& message) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
+        data.writeString16(featureId);
+        data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+        data.writeString16(message);
         remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -59,14 +64,18 @@
     }
 
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-                const String16& packageName, bool startIfModeDefault) {
+                const String16& packageName, const std::unique_ptr<String16>& featureId,
+                bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
+        data.writeString16(featureId);
         data.writeInt32(startIfModeDefault ? 1 : 0);
+        data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
+        data.writeString16(message);
         remote()->transact(START_OPERATION_TRANSACTION, data, &reply);
         // fail on exception
         if (reply.readExceptionCode() != 0) return MODE_ERRORED;
@@ -74,13 +83,14 @@
     }
 
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName) {
+            const String16& packageName, const std::unique_ptr<String16>& featureId) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
         data.writeInt32(code);
         data.writeInt32(uid);
         data.writeString16(packageName);
+        data.writeString16(featureId);
         remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
     }
 
@@ -101,17 +111,6 @@
         remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
     }
 
-    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
-        data.writeStrongBinder(clientToken);
-        remote()->transact(GET_TOKEN_TRANSACTION, data, &reply);
-        // fail on exception
-        if (reply.readExceptionCode() != 0) return nullptr;
-        return reply.readStrongBinder();
-    }
-
-
     virtual int32_t permissionToOpCode(const String16& permission) {
         Parcel data, reply;
         data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -137,6 +136,25 @@
         }
         return reply.readInt32();
     }
+
+    virtual void setCameraAudioRestriction(int32_t mode) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+        data.writeInt32(mode);
+        remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply);
+    }
+
+    virtual bool shouldCollectNotes(int32_t opCode) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
+        data.writeInt32(opCode);
+        remote()->transact(SHOULD_COLLECT_NOTES_TRANSACTION, data, &reply);
+        // fail on exception
+        if (reply.readExceptionCode() != 0) {
+            return false;
+        }
+        return reply.readBool();
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService");
@@ -164,7 +182,12 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            int32_t res = noteOperation(code, uid, packageName);
+            std::unique_ptr<String16> featureId;
+            data.readString16(&featureId);
+            bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+            String16 message = data.readString16();
+            int32_t res = noteOperation(code, uid, packageName, featureId,
+                    shouldCollectAsyncNotedOp, message);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
@@ -175,8 +198,13 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
+            std::unique_ptr<String16> featureId;
+            data.readString16(&featureId);
             bool startIfModeDefault = data.readInt32() == 1;
-            int32_t res = startOperation(token, code, uid, packageName, startIfModeDefault);
+            bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
+            String16 message = data.readString16();
+            int32_t res = startOperation(token, code, uid, packageName, featureId,
+                    startIfModeDefault, shouldCollectAsyncNotedOp, message);
             reply->writeNoException();
             reply->writeInt32(res);
             return NO_ERROR;
@@ -187,7 +215,9 @@
             int32_t code = data.readInt32();
             int32_t uid = data.readInt32();
             String16 packageName = data.readString16();
-            finishOperation(token, code, uid, packageName);
+            std::unique_ptr<String16> featureId;
+            data.readString16(&featureId);
+            finishOperation(token, code, uid, packageName, featureId);
             reply->writeNoException();
             return NO_ERROR;
         } break;
@@ -207,14 +237,6 @@
             reply->writeNoException();
             return NO_ERROR;
         } break;
-        case GET_TOKEN_TRANSACTION: {
-            CHECK_INTERFACE(IAppOpsService, data, reply);
-            sp<IBinder> clientToken = data.readStrongBinder();
-            sp<IBinder> token = getToken(clientToken);
-            reply->writeNoException();
-            reply->writeStrongBinder(token);
-            return NO_ERROR;
-        } break;
         case PERMISSION_TO_OP_CODE_TRANSACTION: {
             CHECK_INTERFACE(IAppOpsService, data, reply);
             String16 permission = data.readString16();
@@ -234,6 +256,21 @@
             reply->writeInt32(res);
             return NO_ERROR;
         } break;
+        case SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION: {
+            CHECK_INTERFACE(IAppOpsService, data, reply);
+            const int32_t mode = data.readInt32();
+            setCameraAudioRestriction(mode);
+            reply->writeNoException();
+            return NO_ERROR;
+        } break;
+        case SHOULD_COLLECT_NOTES_TRANSACTION: {
+            CHECK_INTERFACE(IAppOpsService, data, reply);
+            int32_t opCode = data.readInt32();
+            bool shouldCollect = shouldCollectNotes(opCode);
+            reply->writeNoException();
+            reply->writeBool(shouldCollect);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
index 222b32c..c2bb811 100644
--- a/libs/binder/IMemory.cpp
+++ b/libs/binder/IMemory.cpp
@@ -150,10 +150,6 @@
 }
 
 void* IMemory::unsecurePointer() const {
-    return pointer();
-}
-
-void* IMemory::pointer() const {
     ssize_t offset;
     sp<IMemoryHeap> heap = getMemory(&offset);
     void* const base = heap!=nullptr ? heap->base() : MAP_FAILED;
@@ -162,6 +158,8 @@
     return static_cast<char*>(base) + offset;
 }
 
+void* IMemory::pointer() const { return unsecurePointer(); }
+
 size_t IMemory::size() const {
     size_t size;
     getMemory(nullptr, &size);
diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp
index 038e6bf..b21af96 100644
--- a/libs/binder/IUidObserver.cpp
+++ b/libs/binder/IUidObserver.cpp
@@ -56,13 +56,15 @@
         remote()->transact(ON_UID_IDLE_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
-    virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq)
+    virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+            int32_t capability)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IUidObserver::getInterfaceDescriptor());
         data.writeInt32((int32_t) uid);
         data.writeInt32(procState);
         data.writeInt64(procStateSeq);
+        data.writeInt32(capability);
         remote()->transact(ON_UID_STATE_CHANGED_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
     }
 };
@@ -104,7 +106,8 @@
             uid_t uid = data.readInt32();
             int32_t procState = data.readInt32();
             int64_t procStateSeq = data.readInt64();
-            onUidStateChanged(uid, procState, procStateSeq);
+            int32_t capability = data.readInt32();
+            onUidStateChanged(uid, procState, procStateSeq, capability);
             return NO_ERROR;
         } break;
         default:
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 822247f..9f8d752 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -422,8 +422,10 @@
         const sp<ProcessState> proc(ProcessState::self());
         // grow objects
         if (mObjectsCapacity < mObjectsSize + numObjects) {
+            if ((size_t) numObjects > SIZE_MAX - mObjectsSize) return NO_MEMORY; // overflow
+            if (mObjectsSize + numObjects > SIZE_MAX / 3) return NO_MEMORY; // overflow
             size_t newSize = ((mObjectsSize + numObjects)*3)/2;
-            if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY;   // overflow
+            if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
             binder_size_t *objects =
                 (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
             if (objects == (binder_size_t*)nullptr) {
@@ -1276,8 +1278,10 @@
         if (err != NO_ERROR) return err;
     }
     if (!enoughObjects) {
+        if (mObjectsSize > SIZE_MAX - 2) return NO_MEMORY; // overflow
+        if ((mObjectsSize + 2) > SIZE_MAX / 3) return NO_MEMORY; // overflow
         size_t newSize = ((mObjectsSize+2)*3)/2;
-        if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY;   // overflow
+        if (newSize > SIZE_MAX / sizeof(binder_size_t)) return NO_MEMORY; // overflow
         binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
         if (objects == nullptr) return NO_MEMORY;
         mObjects = objects;
@@ -2283,6 +2287,22 @@
             mObjectsSize = 0;
             break;
         }
+        const flat_binder_object* flat
+            = reinterpret_cast<const flat_binder_object*>(mData + offset);
+        uint32_t type = flat->hdr.type;
+        if (!(type == BINDER_TYPE_BINDER || type == BINDER_TYPE_HANDLE ||
+              type == BINDER_TYPE_FD)) {
+            // We should never receive other types (eg BINDER_TYPE_FDA) as long as we don't support
+            // them in libbinder. If we do receive them, it probably means a kernel bug; try to
+            // recover gracefully by clearing out the objects, and releasing the objects we do
+            // know about.
+            android_errorWriteLog(0x534e4554, "135930648");
+            ALOGE("%s: unsupported type object (%" PRIu32 ") at offset %" PRIu64 "\n",
+                  __func__, type, (uint64_t)offset);
+            releaseObjects();
+            mObjectsSize = 0;
+            break;
+        }
         minOffset = offset + sizeof(flat_binder_object);
     }
     scanForFds();
@@ -2389,6 +2409,8 @@
         return BAD_VALUE;
     }
 
+    if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
+    if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
     size_t newSize = ((mDataSize+len)*3)/2;
     return (newSize <= mDataSize)
             ? (status_t) NO_MEMORY
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index c0f5e31..bdc2e40 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -403,7 +403,9 @@
         }
     }
 
+#ifdef __ANDROID__
     LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened.  Terminating.", driver);
+#endif
 }
 
 ProcessState::~ProcessState()
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index a7a7292..618f88c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -87,4 +87,7 @@
      * package.
      */
     @utf8InCpp String getModuleMetadataPackageName();
+
+    /* Returns the names of all packages. */
+    @utf8InCpp String[] getAllPackages();
 }
diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/fuzzer/Android.bp
new file mode 100644
index 0000000..d2b4d52
--- /dev/null
+++ b/libs/binder/fuzzer/Android.bp
@@ -0,0 +1,47 @@
+cc_fuzz {
+    name: "binder_parcel_fuzzer",
+    defaults: ["libbinder_ndk_host_user"],
+    host_supported: true,
+
+    fuzz_config: {
+        cc: ["smoreland@google.com"],
+    },
+
+    srcs: [
+        "binder.cpp",
+        "binder_ndk.cpp",
+        "hwbinder.cpp",
+        "main.cpp",
+        "util.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libcgrouprc",
+        "libcgrouprc_format",
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libprocessgroup",
+        "libjsoncpp",
+        "libutils",
+    ],
+
+    target: {
+        android: {
+            shared_libs: [
+                "libbinder_ndk",
+                "libbinder",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libbinder_ndk",
+                "libbinder",
+            ],
+        },
+    },
+    // This flag enables verbose output in the fuzz target, and is very useful
+    // for debugging a failure. If you are trying to diagnose how a crash was
+    // produced, you may find uncommenting the below line very useful.
+    // cflags: ["-DENABLE_LOG_FUZZ"],
+}
diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp
new file mode 100644
index 0000000..52c730c
--- /dev/null
+++ b/libs/binder/fuzzer/binder.cpp
@@ -0,0 +1,255 @@
+/*
+ * 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 FUZZ_LOG_TAG "binder"
+
+#include "binder.h"
+#include "util.h"
+
+#include <android/os/IServiceManager.h>
+
+using ::android::status_t;
+
+enum ByteEnum : int8_t {};
+enum IntEnum : int32_t {};
+enum LongEnum : int64_t {};
+
+class ExampleParcelable : public android::Parcelable {
+public:
+    status_t writeToParcel(android::Parcel* /*parcel*/) const override {
+        FUZZ_LOG() << "should not reach";
+        abort();
+    }
+    status_t readFromParcel(const android::Parcel* parcel) override {
+        mExampleExtraField++;
+        return parcel->readInt64(&(this->mExampleUsedData));
+    }
+private:
+    int64_t mExampleExtraField = 0;
+    int64_t mExampleUsedData = 0;
+};
+
+struct ExampleFlattenable : public android::Flattenable<ExampleFlattenable> {
+public:
+    size_t getFlattenedSize() const { return sizeof(mValue); }
+    size_t getFdCount() const { return 0; }
+    status_t flatten(void*& /*buffer*/, size_t& /*size*/, int*& /*fds*/, size_t& /*count*/) const {
+        FUZZ_LOG() << "should not reach";
+        abort();
+    }
+    status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) {
+        if (size < sizeof(mValue)) {
+            return android::NO_MEMORY;
+        }
+        android::FlattenableUtils::read(buffer, size, mValue);
+        return android::OK;
+    }
+private:
+    int32_t mValue = 0xFEEDBEEF;
+};
+
+struct ExampleLightFlattenable : public android::LightFlattenablePod<ExampleLightFlattenable> {
+    int32_t mValue = 0;
+};
+
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+    [] (const ::android::Parcel& p, uint8_t /*data*/) {\
+        FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
+        T t{};\
+        status_t status = p.FUN(&t);\
+        FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;\
+    }
+
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+    [] (const ::android::Parcel& p, uint8_t /*data*/) {\
+        FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
+        T t = p.FUN();\
+        (void) t;\
+        FUZZ_LOG() << #T " done " /* << " value: " << t*/;\
+    }
+
+#define PARCEL_READ_OPT_STATUS(T, FUN) \
+    PARCEL_READ_WITH_STATUS(T, FUN), \
+    PARCEL_READ_NO_STATUS(T, FUN)
+
+// clang-format off
+std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS {
+    PARCEL_READ_NO_STATUS(size_t, dataSize),
+    PARCEL_READ_NO_STATUS(size_t, dataAvail),
+    PARCEL_READ_NO_STATUS(size_t, dataPosition),
+    PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+    [] (const ::android::Parcel& p, uint8_t pos) {
+        FUZZ_LOG() << "about to setDataPosition: " << pos;
+        p.setDataPosition(pos);
+        FUZZ_LOG() << "setDataPosition done";
+    },
+    PARCEL_READ_NO_STATUS(size_t, allowFds),
+    PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
+    [] (const ::android::Parcel& p, uint8_t len) {
+        std::string interface(len, 'a');
+        FUZZ_LOG() << "about to enforceInterface: " << interface;
+        bool b = p.enforceInterface(::android::String16(interface.c_str()));
+        FUZZ_LOG() << "enforced interface: " << b;
+    },
+    [] (const ::android::Parcel& p, uint8_t /*len*/) {
+        FUZZ_LOG() << "about to checkInterface";
+        android::sp<android::IBinder> aBinder = new android::BBinder();
+        bool b = p.checkInterface(aBinder.get());
+        FUZZ_LOG() << "checked interface: " << b;
+    },
+    PARCEL_READ_NO_STATUS(size_t, objectsCount),
+    PARCEL_READ_NO_STATUS(status_t, errorCheck),
+    [] (const ::android::Parcel& p, uint8_t len) {
+        FUZZ_LOG() << "about to read void*";
+        std::vector<uint8_t> data(len);
+        status_t status = p.read(data.data(), len);
+        FUZZ_LOG() << "read status: " << status;
+    },
+    [] (const ::android::Parcel& p, uint8_t len) {
+        FUZZ_LOG() << "about to readInplace";
+        const void* r = p.readInplace(len);
+        FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << hexString(r, len);
+    },
+    PARCEL_READ_OPT_STATUS(int32_t, readInt32),
+    PARCEL_READ_OPT_STATUS(uint32_t, readUint32),
+    PARCEL_READ_OPT_STATUS(int64_t, readInt64),
+    PARCEL_READ_OPT_STATUS(uint64_t, readUint64),
+    PARCEL_READ_OPT_STATUS(float, readFloat),
+    PARCEL_READ_OPT_STATUS(double, readDouble),
+    PARCEL_READ_OPT_STATUS(intptr_t, readIntPtr),
+    PARCEL_READ_OPT_STATUS(bool, readBool),
+    PARCEL_READ_OPT_STATUS(char16_t, readChar),
+    PARCEL_READ_OPT_STATUS(int8_t, readByte),
+
+    PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16),
+    [] (const ::android::Parcel& p, uint8_t /*data*/) {
+        FUZZ_LOG() << "about to read c-str";
+        const char* str = p.readCString();
+        FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>");
+    },
+    PARCEL_READ_OPT_STATUS(android::String8, readString8),
+    PARCEL_READ_OPT_STATUS(android::String16, readString16),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16),
+    [] (const ::android::Parcel& p, uint8_t /*data*/) {
+        FUZZ_LOG() << "about to readString16Inplace";
+        size_t outLen = 0;
+        const char16_t* str = p.readString16Inplace(&outLen);
+        FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen)
+                   << " size: " << outLen;
+    },
+    PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder),
+    PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder),
+
+    // TODO(b/131868573): can force read of arbitrarily sized vector
+    // PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector),
+
+    // only reading one parcelable type for now
+    // TODO(b/131868573): can force read of arbitrarily sized vector
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector),
+    PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable),
+
+    // only reading one binder type for now
+    PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
+    PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
+
+    // TODO(b/131868573): can force read of arbitrarily sized vector
+    // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector),
+
+    // TODO(b/131868573): can force read of arbitrarily sized vector
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector),
+    // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector),
+    // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector),
+    // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector),
+    // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector),
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector),
+    // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
+
+    [] (const android::Parcel& p, uint8_t /*len*/) {
+        FUZZ_LOG() << "about to read flattenable";
+        ExampleFlattenable f;
+        status_t status = p.read(f);
+        FUZZ_LOG() << "read flattenable: " << status;
+    },
+    [] (const android::Parcel& p, uint8_t /*len*/) {
+        FUZZ_LOG() << "about to read lite flattenable";
+        ExampleLightFlattenable f;
+        status_t status = p.read(f);
+        FUZZ_LOG() << "read lite flattenable: " << status;
+    },
+
+    // TODO(b/131868573): can force read of arbitrarily sized vector
+    // TODO: resizeOutVector
+
+    PARCEL_READ_NO_STATUS(int32_t, readExceptionCode),
+    [] (const android::Parcel& p, uint8_t /*len*/) {
+        FUZZ_LOG() << "about to readNativeHandle";
+        native_handle_t* t = p.readNativeHandle();
+        FUZZ_LOG() << "readNativeHandle: " << t;
+        if (t != nullptr) {
+            FUZZ_LOG() << "about to free readNativeHandle";
+            native_handle_close(t);
+            native_handle_delete(t);
+            FUZZ_LOG() << "readNativeHandle freed";
+        }
+    },
+    PARCEL_READ_NO_STATUS(int, readFileDescriptor),
+    PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor),
+    PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor),
+
+    // TODO(b/131868573): can force read of arbitrarily sized vector
+    // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector),
+    // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector),
+
+    [] (const android::Parcel& p, uint8_t len) {
+        FUZZ_LOG() << "about to readBlob";
+        ::android::Parcel::ReadableBlob blob;
+        status_t status = p.readBlob(len, &blob);
+        FUZZ_LOG() << "readBlob status: " << status;
+    },
+    [] (const android::Parcel& p, uint8_t options) {
+        FUZZ_LOG() << "about to readObject";
+        bool nullMetaData = options & 0x1;
+        const void* obj = static_cast<const void*>(p.readObject(nullMetaData));
+        FUZZ_LOG() << "readObject: " << obj;
+    },
+    PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid),
+    PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize),
+    PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize),
+};
+// clang-format on
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl b/libs/binder/fuzzer/binder.h
similarity index 65%
copy from cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
copy to libs/binder/fuzzer/binder.h
index 7f74ceb..b224ef4 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
+++ b/libs/binder/fuzzer/binder.h
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package android.os;
+#include <binder/Parcel.h>
+#include <vector>
 
-/**
-  * Token used by the IDumpstateListener to watch for dumpstate death.
-  * {@hide}
-  */
-interface IDumpstateToken {
-}
+#include "parcel_fuzzer.h"
+
+extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/fuzzer/binder_ndk.cpp
new file mode 100644
index 0000000..29da8f7
--- /dev/null
+++ b/libs/binder/fuzzer/binder_ndk.cpp
@@ -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.
+ */
+#define FUZZ_LOG_TAG "binder_ndk"
+
+#include "binder_ndk.h"
+
+#include <android/binder_parcel_utils.h>
+
+#include "util.h"
+
+// TODO(b/142061461): parent class
+class SomeParcelable {
+public:
+    binder_status_t readFromParcel(const AParcel* parcel) {
+        return AParcel_readInt32(parcel, &mValue);
+    }
+
+private:
+    int32_t mValue = 0;
+};
+
+#define PARCEL_READ(T, FUN)                                              \
+    [](const NdkParcelAdapter& p, uint8_t /*data*/) {                    \
+        FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
+        T t{};                                                           \
+        binder_status_t status = FUN(p.aParcel(), &t);                   \
+        FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;  \
+    }
+
+// clang-format off
+std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{
+        // methods from binder_parcel.h
+        [](const NdkParcelAdapter& p, uint8_t pos) {
+            FUZZ_LOG() << "about to set data position to " << pos;
+            binder_status_t status = AParcel_setDataPosition(p.aParcel(), pos);
+            FUZZ_LOG() << "set data position: " << status;
+        },
+        [](const NdkParcelAdapter& p, uint8_t /*data*/) {
+            FUZZ_LOG() << "about to read status header";
+            ndk::ScopedAStatus t;
+            binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR());
+            FUZZ_LOG() << "read status header: " << status;
+        },
+        PARCEL_READ(int32_t, AParcel_readInt32),
+        PARCEL_READ(uint32_t, AParcel_readUint32),
+        PARCEL_READ(int64_t, AParcel_readInt64),
+        PARCEL_READ(uint64_t, AParcel_readUint64),
+        PARCEL_READ(float, AParcel_readFloat),
+        PARCEL_READ(double, AParcel_readDouble),
+        PARCEL_READ(bool, AParcel_readBool),
+        PARCEL_READ(char16_t, AParcel_readChar),
+        PARCEL_READ(int8_t, AParcel_readByte),
+
+        // methods from binder_parcel_utils.h
+        PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readNullableStrongBinder),
+        PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readRequiredStrongBinder),
+        PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readNullableParcelFileDescriptor),
+        PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readRequiredParcelFileDescriptor),
+        PARCEL_READ(std::string, ndk::AParcel_readString),
+        PARCEL_READ(std::optional<std::string>, ndk::AParcel_readString),
+        // TODO(b/131868573): can force process to allocate arbitrary amount of
+        // memory
+        // PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>,
+        // ndk::AParcel_readVector), PARCEL_READ(std::vector<SomeParcelable>,
+        // ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<float>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<double>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
+        // PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
+        // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+};
+// clang-format on
diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/fuzzer/binder_ndk.h
new file mode 100644
index 0000000..622cafc
--- /dev/null
+++ b/libs/binder/fuzzer/binder_ndk.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.
+ */
+
+#include <android/binder_auto_utils.h>
+#include <vector>
+
+#include <android/binder_parcel.h>
+#include "parcel_fuzzer.h"
+
+// libbinder_ndk doesn't export this header which breaks down its API for NDK
+// and APEX users, but we need access to it to fuzz.
+#include "../ndk/parcel_internal.h"
+
+class NdkParcelAdapter {
+public:
+    NdkParcelAdapter() : mParcel(new AParcel(nullptr /*binder*/)) {}
+
+    const AParcel* aParcel() const { return mParcel.get(); }
+    AParcel* aParcel() { return mParcel.get(); }
+
+    size_t dataSize() const { return aParcel()->get()->dataSize(); }
+    size_t dataAvail() const { return aParcel()->get()->dataAvail(); }
+    size_t dataPosition() const { return aParcel()->get()->dataPosition(); }
+    size_t dataCapacity() const { return aParcel()->get()->dataCapacity(); }
+    android::status_t setData(const uint8_t* buffer, size_t len) {
+        return aParcel()->get()->setData(buffer, len);
+    }
+
+private:
+    ndk::ScopedAParcel mParcel;
+};
+
+extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/fuzzer/hwbinder.cpp
new file mode 100644
index 0000000..0fec393
--- /dev/null
+++ b/libs/binder/fuzzer/hwbinder.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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 FUZZ_LOG_TAG "hwbinder"
+
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <hwbinder/Parcel.h>
+
+using ::android::status_t;
+
+// TODO: support scatter-gather types
+
+std::ostream& operator<<(std::ostream& os, const ::android::sp<::android::hardware::IBinder>& binder) {
+    os << binder.get();
+    return os;
+}
+
+#define PARCEL_READ_OPT_STATUS(T, FUN) \
+    PARCEL_READ_NO_STATUS(T, FUN), PARCEL_READ_WITH_STATUS(T, FUN)
+
+#define PARCEL_READ_NO_STATUS(T, FUN) \
+    [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
+        FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\
+        T t = p.FUN();\
+        FUZZ_LOG() << #T " value: " << t;\
+    }
+
+#define PARCEL_READ_WITH_STATUS(T, FUN) \
+    [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\
+        FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\
+        T t;\
+        status_t status = p.FUN(&t);\
+        FUZZ_LOG() << #T " status: " << status << " value: " << t;\
+    }
+
+// clang-format off
+std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS {
+    PARCEL_READ_NO_STATUS(size_t, dataSize),
+    PARCEL_READ_NO_STATUS(size_t, dataAvail),
+    PARCEL_READ_NO_STATUS(size_t, dataPosition),
+    PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+    [] (const ::android::hardware::Parcel& p, uint8_t pos) {
+        FUZZ_LOG() << "about to setDataPosition: " << pos;
+        p.setDataPosition(pos);
+        FUZZ_LOG() << "setDataPosition done";
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t length) {
+        FUZZ_LOG() << "about to enforceInterface";
+        std::string interfaceName(length, 'a');
+        bool okay = p.enforceInterface(interfaceName.c_str());
+        FUZZ_LOG() << "enforceInterface status: " << okay;
+    },
+    PARCEL_READ_NO_STATUS(size_t, objectsCount),
+    [] (const ::android::hardware::Parcel& p, uint8_t length) {
+        FUZZ_LOG() << "about to read";
+        std::vector<uint8_t> data (length);
+        status_t status = p.read(data.data(), length);
+        FUZZ_LOG() << "read status: " << status << " data: " << hexString(data.data(), data.size());
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t length) {
+        FUZZ_LOG() << "about to read";
+        std::vector<uint8_t> data (length);
+        const void* inplace = p.readInplace(length);
+        FUZZ_LOG() << "read status: " << hexString(inplace, length);
+    },
+    PARCEL_READ_WITH_STATUS(int8_t, readInt8),
+    PARCEL_READ_WITH_STATUS(uint8_t, readUint8),
+    PARCEL_READ_WITH_STATUS(int16_t, readInt16),
+    PARCEL_READ_WITH_STATUS(uint16_t, readUint16),
+    PARCEL_READ_OPT_STATUS(int32_t, readInt32),
+    PARCEL_READ_OPT_STATUS(uint32_t, readUint32),
+    PARCEL_READ_OPT_STATUS(int64_t, readInt64),
+    PARCEL_READ_OPT_STATUS(uint64_t, readUint64),
+    PARCEL_READ_OPT_STATUS(float, readFloat),
+    PARCEL_READ_OPT_STATUS(double, readDouble),
+    PARCEL_READ_OPT_STATUS(bool, readBool),
+    [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+        FUZZ_LOG() << "about to readCString";
+        const char* str = p.readCString();
+        FUZZ_LOG() << "readCString " << (str ? str : "<null>");
+    },
+    PARCEL_READ_OPT_STATUS(::android::String16, readString16),
+    PARCEL_READ_WITH_STATUS(std::unique_ptr<::android::String16>, readString16),
+    [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+        FUZZ_LOG() << "about to readString16Inplace";
+        size_t outSize = 0;
+        const char16_t* str = p.readString16Inplace(&outSize);
+        FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outSize);
+    },
+    PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder),
+    PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder),
+    [] (const ::android::hardware::Parcel& p, uint8_t size) {
+        FUZZ_LOG() << "about to readBuffer";
+        size_t handle = 0;
+        const void* data = nullptr;
+        status_t status = p.readBuffer(size, &handle, &data);
+        FUZZ_LOG() << "readBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+        // should be null since we don't create any IPC objects
+        CHECK(data == nullptr) << data;
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t size) {
+        FUZZ_LOG() << "about to readNullableBuffer";
+        size_t handle = 0;
+        const void* data = nullptr;
+        status_t status = p.readNullableBuffer(size, &handle, &data);
+        FUZZ_LOG() << "readNullableBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+        // should be null since we don't create any IPC objects
+        CHECK(data == nullptr) << data;
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t size) {
+        FUZZ_LOG() << "about to readEmbeddedBuffer";
+        size_t handle = 0;
+        size_t parent_buffer_handle = 0;
+        size_t parent_offset = 3;
+        const void* data = nullptr;
+        status_t status = p.readEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data);
+        FUZZ_LOG() << "readEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+        // should be null since we don't create any IPC objects
+        CHECK(data == nullptr) << data;
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t size) {
+        FUZZ_LOG() << "about to readNullableEmbeddedBuffer";
+        size_t handle = 0;
+        size_t parent_buffer_handle = 0;
+        size_t parent_offset = 3;
+        const void* data = nullptr;
+        status_t status = p.readNullableEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data);
+        FUZZ_LOG() << "readNullableEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data;
+
+        // should be null since we don't create any IPC objects
+        CHECK(data == nullptr) << data;
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t size) {
+        FUZZ_LOG() << "about to readEmbeddedNativeHandle";
+        size_t parent_buffer_handle = size & 0xf;
+        size_t parent_offset = size >> 4;
+        const native_handle_t* handle = nullptr;
+        status_t status = p.readEmbeddedNativeHandle(parent_buffer_handle, parent_offset, &handle);
+        FUZZ_LOG() << "readEmbeddedNativeHandle status: " << status << " handle: " << handle << " handle: " << handle;
+
+        // should be null since we don't create any IPC objects
+        CHECK(handle == nullptr) << handle;
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t size) {
+        FUZZ_LOG() << "about to readNullableEmbeddedNativeHandle";
+        size_t parent_buffer_handle = size & 0xf;
+        size_t parent_offset = size >> 4;
+        const native_handle_t* handle = nullptr;
+        status_t status = p.readNullableEmbeddedNativeHandle(parent_buffer_handle, parent_offset, &handle);
+        FUZZ_LOG() << "readNullableEmbeddedNativeHandle status: " << status << " handle: " << handle << " handle: " << handle;
+
+        // should be null since we don't create any IPC objects
+        CHECK(handle == nullptr) << handle;
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+        FUZZ_LOG() << "about to readNativeHandleNoDup";
+        const native_handle_t* handle = nullptr;
+        status_t status = p.readNativeHandleNoDup(&handle);
+        FUZZ_LOG() << "readNativeHandleNoDup status: " << status << " handle: " << handle;
+
+        // should be null since we don't create any IPC objects
+        CHECK(handle == nullptr) << handle;
+        CHECK(status != ::android::OK);
+    },
+    [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {
+        FUZZ_LOG() << "about to readNullableNativeHandleNoDup";
+        const native_handle_t* handle = nullptr;
+        status_t status = p.readNullableNativeHandleNoDup(&handle);
+        FUZZ_LOG() << "readNullableNativeHandleNoDup status: " << status << " handle: " << handle;
+
+        // should be null since we don't create any IPC objects
+        CHECK(handle == nullptr) << handle;
+    },
+};
+// clang-format on
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl b/libs/binder/fuzzer/hwbinder.h
similarity index 64%
copy from cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
copy to libs/binder/fuzzer/hwbinder.h
index 7f74ceb..a6c66be 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
+++ b/libs/binder/fuzzer/hwbinder.h
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,9 @@
  * limitations under the License.
  */
 
-package android.os;
+#include <hwbinder/Parcel.h>
+#include <vector>
 
-/**
-  * Token used by the IDumpstateListener to watch for dumpstate death.
-  * {@hide}
-  */
-interface IDumpstateToken {
-}
+#include "parcel_fuzzer.h"
+
+extern std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS;
diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp
new file mode 100644
index 0000000..6657edb
--- /dev/null
+++ b/libs/binder/fuzzer/main.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 FUZZ_LOG_TAG "main"
+
+#include "binder.h"
+#include "binder_ndk.h"
+#include "hwbinder.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <cstdlib>
+#include <ctime>
+
+template <typename P>
+void doFuzz(
+        const std::vector<ParcelRead<P>>& reads,
+        const std::vector<uint8_t>& input,
+        const std::vector<uint8_t>& instructions) {
+
+    P p;
+    p.setData(input.data(), input.size());
+
+    // since we are only using a byte to index
+    CHECK(reads.size() <= 255) << reads.size();
+
+    for (size_t i = 0; i < instructions.size() - 1; i += 2) {
+        uint8_t a = instructions[i];
+        uint8_t readIdx = a % reads.size();
+
+        uint8_t b = instructions[i + 1];
+
+        FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
+                   << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx)
+                   << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize()
+                   << " avail: " << p.dataAvail() << " pos: " << p.dataPosition()
+                   << " cap: " << p.dataCapacity();
+
+        reads[readIdx](p, b);
+    }
+}
+
+void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) {
+    uint8_t parcelType = options & 0x3;
+
+    switch (parcelType) {
+        case 0x0:
+            doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input,
+                                                instructions);
+            break;
+        case 0x1:
+            doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions);
+            break;
+        case 0x2:
+            doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions);
+            break;
+        case 0x3:
+            /*reserved for future use*/
+            break;
+        default:
+            LOG_ALWAYS_FATAL("unknown parcel type %d", static_cast<int>(parcelType));
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size <= 1) return 0;  // no use
+
+    // avoid timeouts, see b/142617274, b/142473153
+    if (size > 50000) return 0;
+
+    uint8_t options = *data;
+    data++;
+    size--;
+
+    // TODO: generate 'objects' data
+
+    // data to fill out parcel
+    size_t inputLen = size / 2;
+    std::vector<uint8_t> input(data, data + inputLen);
+    data += inputLen;
+    size -= inputLen;
+
+    // data to use to determine what to do
+    size_t instructionLen = size;
+    std::vector<uint8_t> instructions(data, data + instructionLen);
+    data += instructionLen;
+    size -= instructionLen;
+
+    CHECK(size == 0) << "size: " << size;
+
+    FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen;
+    FUZZ_LOG() << "input: " << hexString(input);
+    FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+    fuzz(options, input, instructions);
+    return 0;
+}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl b/libs/binder/fuzzer/parcel_fuzzer.h
similarity index 66%
rename from cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
rename to libs/binder/fuzzer/parcel_fuzzer.h
index 7f74ceb..10cf17c 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateToken.aidl
+++ b/libs/binder/fuzzer/parcel_fuzzer.h
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,11 +14,5 @@
  * limitations under the License.
  */
 
-package android.os;
-
-/**
-  * Token used by the IDumpstateListener to watch for dumpstate death.
-  * {@hide}
-  */
-interface IDumpstateToken {
-}
+template <typename P>
+using ParcelRead = std::function<void(const P& p, uint8_t data)>;
diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/fuzzer/util.cpp
new file mode 100644
index 0000000..479f406
--- /dev/null
+++ b/libs/binder/fuzzer/util.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.
+ */
+#define FUZZ_LOG_TAG "util"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <iomanip>
+#include <sstream>
+
+std::string hexString(const void* bytes, size_t len) {
+    if (bytes == nullptr) return "<null>";
+
+    const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+    char chars[] = "0123456789abcdef";
+    std::string result;
+    result.resize(len * 2);
+
+    for (size_t i = 0; i < len; i++) {
+        result[2 * i] = chars[bytes8[i] >> 4];
+        result[2 * i + 1] = chars[bytes8[i] & 0xf];
+    }
+
+    return result;
+}
+std::string hexString(const std::vector<uint8_t>& bytes) {
+    return hexString(bytes.data(), bytes.size());
+}
diff --git a/libs/binder/fuzzer/util.h b/libs/binder/fuzzer/util.h
new file mode 100644
index 0000000..aa504d2
--- /dev/null
+++ b/libs/binder/fuzzer/util.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.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifndef FUZZ_LOG_TAG
+#error "Must define FUZZ_LOG_TAG"
+#endif
+
+#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG).log()
+
+#ifdef ENABLE_LOG_FUZZ
+class FuzzLog {
+public:
+    FuzzLog(const char* tag) : mTag(tag) {}
+    ~FuzzLog() { std::cout << mTag << ": " << mOs.str() << std::endl; }
+
+    std::stringstream& log() { return mOs; }
+
+private:
+    const char* mTag = nullptr;
+    std::stringstream mOs;
+};
+#else
+class FuzzLog {
+public:
+    FuzzLog(const char* /*tag*/) {}
+    template <typename T>
+    FuzzLog& operator<<(const T& /*t*/) {
+        return *this;
+    }
+    FuzzLog& log() { return *this; }
+};
+#endif
+
+std::string hexString(const void* bytes, size_t len);
+std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index b19cde7..5b6eb68 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -17,12 +17,14 @@
 #ifndef ANDROID_APP_OPS_MANAGER_H
 #define ANDROID_APP_OPS_MANAGER_H
 
-#ifndef __ANDROID_VNDK__
-
 #include <binder/IAppOpsService.h>
 
 #include <utils/threads.h>
 
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -109,6 +111,18 @@
         OP_START_FOREGROUND = 76,
         OP_BLUETOOTH_SCAN = 77,
         OP_USE_BIOMETRIC = 78,
+        OP_ACTIVITY_RECOGNITION = 79,
+        OP_SMS_FINANCIAL_TRANSACTIONS = 80,
+        OP_READ_MEDIA_AUDIO = 81,
+        OP_WRITE_MEDIA_AUDIO = 82,
+        OP_READ_MEDIA_VIDEO = 83,
+        OP_WRITE_MEDIA_VIDEO = 84,
+        OP_READ_MEDIA_IMAGES = 85,
+        OP_WRITE_MEDIA_IMAGES = 86,
+        OP_LEGACY_STORAGE = 87,
+        OP_ACCESS_ACCESSIBILITY = 88,
+        OP_READ_DEVICE_IDENTIFIERS = 89,
+        _NUM_OP = 90
     };
 
     AppOpsManager();
@@ -116,27 +130,39 @@
     int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage);
     int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
             const String16& callingPackage);
+    // @Deprecated, use noteOp(int32_t, int32_t uid, const String16&, const String16&,
+    //              const String16&) instead
     int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
+    int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
+            const std::unique_ptr<String16>& featureId, const String16& message);
+    // @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
+    //              const String16&) instead
     int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
             bool startIfModeDefault);
+    int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
+            bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+            const String16& message);
+    // @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
     void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
+    void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
+            const std::unique_ptr<String16>& featureId);
     void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback);
     void stopWatchingMode(const sp<IAppOpsCallback>& callback);
     int32_t permissionToOpCode(const String16& permission);
+    void setCameraAudioRestriction(int32_t mode);
 
 private:
     Mutex mLock;
     sp<IAppOpsService> mService;
 
     sp<IAppOpsService> getService();
+    bool shouldCollectNotes(int32_t opCode);
 };
 
 
 } // namespace android
+
 // ---------------------------------------------------------------------------
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
 
 #endif // ANDROID_APP_OPS_MANAGER_H
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index b74c623..1b4bcce 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -18,11 +18,13 @@
 #ifndef ANDROID_IAPP_OPS_SERVICE_H
 #define ANDROID_IAPP_OPS_SERVICE_H
 
-#ifndef __ANDROID_VNDK__
-
 #include <binder/IAppOpsCallback.h>
 #include <binder/IInterface.h>
 
+#ifdef __ANDROID_VNDK__
+#error "This header is not visible to vendors"
+#endif
+
 namespace android {
 
 // ----------------------------------------------------------------------
@@ -33,18 +35,22 @@
     DECLARE_META_INTERFACE(AppOpsService)
 
     virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
-    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
+    virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
+            const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+            const String16& message) = 0;
     virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName, bool startIfModeDefault) = 0;
+            const String16& packageName, const std::unique_ptr<String16>& featureId,
+            bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
     virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
-            const String16& packageName) = 0;
+            const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
     virtual void startWatchingMode(int32_t op, const String16& packageName,
             const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
-    virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
     virtual int32_t permissionToOpCode(const String16& permission) = 0;
     virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
             const String16& packageName) = 0;
+    virtual void setCameraAudioRestriction(int32_t mode) = 0;
+    virtual bool shouldCollectNotes(int32_t opCode) = 0;
 
     enum {
         CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
@@ -53,9 +59,10 @@
         FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
         START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
         STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
-        GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
-        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
-        CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+        PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
+        CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+        SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+        SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
     };
 
     enum {
@@ -81,8 +88,4 @@
 
 } // namespace android
 
-#else // __ANDROID_VNDK__
-#error "This header is not visible to vendors"
-#endif // __ANDROID_VNDK__
-
 #endif // ANDROID_IAPP_OPS_SERVICE_H
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 79d9b79..3a401ad 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -253,7 +253,6 @@
   "android.media.IDrmClient",
   "android.media.IEffect",
   "android.media.IEffectClient",
-  "android.media.IMediaAnalyticsService",
   "android.media.IMediaCodecList",
   "android.media.IMediaDrmService",
   "android.media.IMediaExtractor",
@@ -262,6 +261,7 @@
   "android.media.IMediaHTTPService",
   "android.media.IMediaLogService",
   "android.media.IMediaMetadataRetriever",
+  "android.media.IMediaMetricsService",
   "android.media.IMediaPlayer",
   "android.media.IMediaPlayerClient",
   "android.media.IMediaPlayerService",
diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h
index 98e92c4..1a36eb0 100644
--- a/libs/binder/include/binder/IMemory.h
+++ b/libs/binder/include/binder/IMemory.h
@@ -76,13 +76,34 @@
     // NOLINTNEXTLINE(google-default-arguments)
     virtual sp<IMemoryHeap> getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const = 0;
 
+    // helpers
+
+    // Accessing the underlying pointer must be done with caution, as there are
+    // some inherent security risks associated with it. When receiving an
+    // IMemory from an untrusted process, there is currently no way to guarantee
+    // that this process would't change the content after the fact. This may
+    // lead to TOC/TOU class of security bugs. In most cases, when performance
+    // is not an issue, the recommended practice is to immediately copy the
+    // buffer upon reception, then work with the copy, e.g.:
+    //
+    // std::string private_copy(mem.size(), '\0');
+    // memcpy(private_copy.data(), mem.unsecurePointer(), mem.size());
+    //
+    // In cases where performance is an issue, this matter must be addressed on
+    // an ad-hoc basis.
     void* unsecurePointer() const;
 
-    // helpers
-    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
-    void* pointer() const;
     size_t size() const;
     ssize_t offset() const;
+
+private:
+    // These are now deprecated and are left here for backward-compatibility
+    // with prebuilts that may reference these symbol at runtime.
+    // Instead, new code should use unsecurePointer()/unsecureFastPointer(),
+    // which do the same thing, but make it more obvious that there are some
+    // security-related pitfalls associated with them.
+    void* pointer() const;
+    void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
 };
 
 class BnMemory : public BnInterface<IMemory>
diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h
index 09e50a9..d070390 100644
--- a/libs/binder/include/binder/IUidObserver.h
+++ b/libs/binder/include/binder/IUidObserver.h
@@ -34,7 +34,8 @@
     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;
+    virtual void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
+            int32_t capability) = 0;
 
     enum {
         ON_UID_GONE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index e66e425..b37db43 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -29,7 +29,7 @@
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libbinder_ndk",
 
     defaults: ["libbinder_ndk_host_user"],
@@ -72,6 +72,12 @@
     ],
 
     target: {
+        android: {
+            // Only one copy of this library on an Android device
+            static: {
+                enabled: false,
+            },
+        },
         linux: {
             version_script: "libbinder_ndk.map.txt",
         },
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 5e0574a..94ab9f0 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -73,6 +73,7 @@
     BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION,
     BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION,
     BINDER_LIB_TEST_ECHO_VECTOR,
+    BINDER_LIB_TEST_REJECT_BUF,
 };
 
 pid_t start_server_process(int arg2, bool usePoll = false)
@@ -1025,6 +1026,34 @@
     EXPECT_EQ(readValue, testValue);
 }
 
+TEST_F(BinderLibTest, BufRejected) {
+    Parcel data, reply;
+    uint32_t buf;
+    sp<IBinder> server = addServer();
+    ASSERT_TRUE(server != nullptr);
+
+    binder_buffer_object obj {
+        .hdr = { .type = BINDER_TYPE_PTR },
+        .flags = 0,
+        .buffer = reinterpret_cast<binder_uintptr_t>((void*)&buf),
+        .length = 4,
+    };
+    data.setDataCapacity(1024);
+    // Write a bogus object at offset 0 to get an entry in the offset table
+    data.writeFileDescriptor(0);
+    EXPECT_EQ(data.objectsCount(), 1);
+    uint8_t *parcelData = const_cast<uint8_t*>(data.data());
+    // And now, overwrite it with the buffer object
+    memcpy(parcelData, &obj, sizeof(obj));
+    data.setDataSize(sizeof(obj));
+
+    status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply);
+    // Either the kernel should reject this transaction (if it's correct), but
+    // if it's not, the server implementation should return an error if it
+    // finds an object in the received Parcel.
+    EXPECT_NE(NO_ERROR, ret);
+}
+
 class BinderLibTestService : public BBinder
 {
     public:
@@ -1307,6 +1336,9 @@
                 reply->writeUint64Vector(vector);
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_REJECT_BUF: {
+                return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
+            }
             default:
                 return UNKNOWN_TRANSACTION;
             };
@@ -1338,6 +1370,9 @@
          */
         testService->setExtension(new BBinder());
 
+        // Required for test "BufRejected'
+        testService->setRequestingSid(true);
+
         /*
          * We need this below, but can't hold a sp<> because it prevents the
          * node from being cleaned up automatically. It's safe in this case
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
new file mode 100644
index 0000000..bab2674
--- /dev/null
+++ b/libs/bufferqueueconverter/Android.bp
@@ -0,0 +1,28 @@
+cc_library_headers {
+    name: "libbufferqueueconverter_headers",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+}
+
+cc_library_shared {
+    name: "libbufferqueueconverter",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+    double_loadable: true,
+
+    srcs: [
+        "BufferQueueConverter.cpp",
+    ],
+
+    shared_libs: [
+        "libgui",
+        "libui",
+        "libutils",
+        "libbinder",
+        "libbase",
+        "liblog",
+    ],
+    export_include_dirs: ["include"],
+}
diff --git a/libs/bufferqueueconverter/BufferQueueConverter.cpp b/libs/bufferqueueconverter/BufferQueueConverter.cpp
new file mode 100644
index 0000000..b1896fa
--- /dev/null
+++ b/libs/bufferqueueconverter/BufferQueueConverter.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/Surface.h>
+#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
+
+#include "include/bufferqueueconverter/BufferQueueConverter.h"
+
+
+using ::android::Surface;
+using ::android::IGraphicBufferProducer;
+using ::android::hardware::graphics::bufferqueue::V2_0::utils::H2BGraphicBufferProducer;
+
+
+namespace android {
+
+struct SurfaceHolder {
+    sp<Surface> surface;
+    SurfaceHolder(const sp<Surface>& s) : surface(s) {}
+};
+
+/**
+ * Custom deleter for SurfaceHolder unique pointer
+ */
+void destroySurfaceHolder(SurfaceHolder* surfaceHolder) {
+    delete surfaceHolder;
+}
+
+
+SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token) {
+    if (token == nullptr) {
+        ALOGE("Passed IGraphicBufferProducer handle is invalid.");
+        return SurfaceHolderUniquePtr(nullptr, nullptr);
+    }
+
+    sp<IGraphicBufferProducer> bufferProducer = new H2BGraphicBufferProducer(token);
+    if (bufferProducer == nullptr) {
+        ALOGE("Failed to get IGraphicBufferProducer.");
+        return SurfaceHolderUniquePtr(nullptr, nullptr);
+    }
+
+    sp<Surface> newSurface(new Surface(bufferProducer, true));
+    if (newSurface == nullptr) {
+        ALOGE("Failed to create Surface from HGBP.");
+        return SurfaceHolderUniquePtr(nullptr, nullptr);
+    }
+
+    return SurfaceHolderUniquePtr(new SurfaceHolder(newSurface), destroySurfaceHolder);
+}
+
+
+ANativeWindow* getNativeWindow(SurfaceHolder* handle) {
+    if (handle == nullptr) {
+        ALOGE("SurfaceHolder is invalid.");
+        return nullptr;
+    }
+
+    return static_cast<ANativeWindow*>(handle->surface.get());
+}
+
+} // namespace android
diff --git a/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
new file mode 100644
index 0000000..84bceba
--- /dev/null
+++ b/libs/bufferqueueconverter/include/bufferqueueconverter/BufferQueueConverter.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_QUEUE_CONVERTER_H
+#define ANDROID_BUFFER_QUEUE_CONVERTER_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <android/native_window.h>
+
+using ::android::sp;
+using HGraphicBufferProducer =
+      ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
+
+namespace android {
+    /**
+     * Opaque handle for a data structure holding Surface.
+     */
+    typedef struct SurfaceHolder SurfaceHolder;
+
+    /**
+     * SurfaceHolder unique pointer type
+     */
+    using SurfaceHolderUniquePtr = std::unique_ptr<SurfaceHolder, void(*)(SurfaceHolder*)>;
+
+    /**
+     * Returns a SurfaceHolder that wraps a Surface generated from a given HGBP.
+     *
+     * @param  token         Hardware IGraphicBufferProducer to create a
+     *                       Surface.
+     * @return SurfaceHolder Unique pointer to created SurfaceHolder object.
+     */
+    SurfaceHolderUniquePtr getSurfaceFromHGBP(const sp<HGraphicBufferProducer>& token);
+
+    /**
+     * Returns ANativeWindow pointer from a given SurfaceHolder.  Returned
+     * pointer is valid only while the containing SurfaceHolder is alive.
+     *
+     * @param  surfaceHolder  SurfaceHolder to generate a native window.
+     * @return ANativeWindow* a pointer to a generated native window.
+     */
+    ANativeWindow* getNativeWindow(SurfaceHolder* surfaceHolder);
+
+} // namespace android
+
+#endif // ANDROID_BUFFER_QUEUE_CONVERTER_H
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 05a462e..b6b81bb 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -486,7 +486,7 @@
         if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false;
     }
 
-    concurrent_val_t czeros = {.policy = {0}, .active = {0}};
+    concurrent_val_t czeros = { .active = {0}, .policy = {0}, };
     std::vector<concurrent_val_t> cvals(gNCpus, czeros);
     for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
         if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT)
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
new file mode 100644
index 0000000..00f7484
--- /dev/null
+++ b/libs/gralloc/types/Android.bp
@@ -0,0 +1,55 @@
+// 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.
+
+cc_library {
+    name: "libgralloctypes",
+    defaults: ["libbinder_ndk_host_user"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-enum-compare",
+    ],
+    host_supported: true,
+
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+
+    srcs: [
+        "Gralloc4.cpp"
+    ],
+
+    shared_libs: [
+        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.mapper@4.0",
+        "libhidlbase",
+        "liblog",
+    ],
+
+    export_shared_lib_headers: [
+        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.mapper@4.0",
+        "libhidlbase",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    sanitize: {
+        misc_undefined: ["integer"],
+    },
+}
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
new file mode 100644
index 0000000..603e7e5
--- /dev/null
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -0,0 +1,1327 @@
+/*
+ * 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 "libgralloctypes"
+
+#include <cstring>
+#include <cinttypes>
+#include <limits>
+
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+
+#include "gralloctypes/Gralloc4.h"
+
+using android::hardware::hidl_vec;
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
+
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+namespace gralloc4 {
+
+static inline bool hasAdditionOverflow(size_t a, size_t b) {
+    return a > SIZE_MAX - b;
+}
+
+/**
+ * OutputHidlVec represents the hidl_vec that is outputed when a type is encoded into a byte stream.
+ * This class is used to track the current state of a hidl_vec as it is filled with the encoded
+ * byte stream.
+ *
+ * This type is needed because hidl_vec's resize() allocates a new backing array every time.
+ * This type does not need an copies and only needs one resize operation.
+ */
+class OutputHidlVec {
+public:
+    OutputHidlVec(hidl_vec<uint8_t>* vec)
+        : mVec(vec) {}
+
+    status_t resize() {
+        if (!mVec) {
+            return BAD_VALUE;
+        }
+        mVec->resize(mNeededResize);
+        mResized = true;
+        return NO_ERROR;
+    }
+
+    status_t encode(const uint8_t* data, size_t size) {
+        if (!mVec) {
+            return BAD_VALUE;
+        }
+        if (!mResized) {
+            if (hasAdditionOverflow(mNeededResize, size)) {
+                clear();
+                return BAD_VALUE;
+            }
+            /**
+             * Update mNeededResize and return NO_ERROR here because if (!mResized), the
+             * caller hasn't called resize(). No data will be written into the mVec until
+             * the caller resizes. We can't resize here for the caller because hidl_vec::resize()
+             * allocates a new backing array every time.
+             */
+            mNeededResize += size;
+            return NO_ERROR;
+        }
+
+        if (hasAdditionOverflow(mOffset, size) || (mVec->size() < size + mOffset)) {
+            clear();
+            return BAD_VALUE;
+        }
+
+        std::copy(data, data + size, mVec->data() + mOffset);
+
+        mOffset += size;
+        return NO_ERROR;
+    }
+
+    void clear() {
+        if (mVec) {
+            mVec->resize(0);
+        }
+        mNeededResize = 0;
+        mResized = false;
+        mOffset = 0;
+    }
+
+private:
+    hidl_vec<uint8_t>* mVec;
+    size_t mNeededResize = 0;
+    size_t mResized = false;
+    size_t mOffset = 0;
+};
+
+/**
+ * InputHidlVec represents the hidl_vec byte stream that is inputed when a type is decoded.
+ * This class is used to track the current index of the byte stream of the hidl_vec as it is
+ * decoded.
+ */
+class InputHidlVec {
+public:
+    InputHidlVec(const hidl_vec<uint8_t>* vec)
+        : mVec(vec) {}
+
+    status_t decode(uint8_t* data, size_t size) {
+        if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+            return BAD_VALUE;
+        }
+
+        std::copy(mVec->data() + mOffset, mVec->data() + mOffset + size, data);
+
+        mOffset += size;
+        return NO_ERROR;
+    }
+
+    status_t decode(std::string* string, size_t size) {
+        if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+            return BAD_VALUE;
+        }
+
+        string->assign(mVec->data() + mOffset, mVec->data() + mOffset + size);
+
+        mOffset += size;
+        return NO_ERROR;
+    }
+
+    bool hasRemainingData() {
+        if (!mVec) {
+            return false;
+        }
+        return mVec->size() > mOffset;
+    }
+
+    size_t getRemainingSize() {
+        if (!mVec) {
+            return 0;
+        }
+        return mVec->size() - mOffset;
+    }
+
+private:
+    const hidl_vec<uint8_t>* mVec;
+    size_t mOffset = 0;
+};
+
+/**
+ * EncodeHelper is a function type that encodes T into the OutputHidlVec.
+ */
+template<class T>
+using EncodeHelper = status_t(*)(const T&, OutputHidlVec*);
+
+/**
+ * DecodeHelper is a function type that decodes InputHidlVec into T.
+ */
+template<class T>
+using DecodeHelper = status_t(*)(InputHidlVec*, T*);
+
+/**
+ * ErrorHandler is a function type that is called when the corresponding DecodeHelper function
+ * fails. ErrorHandler cleans up the object T so the caller doesn't receive a partially created
+ * T.
+ */
+template<class T>
+using ErrorHandler = void(*)(T*);
+
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output);
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
+
+/**
+ * encode/encodeMetadata are the main encoding functions. They take in T and uses the encodeHelper
+ * function to turn T into the hidl_vec byte stream.
+ *
+ * These functions first call the encodeHelper function to determine how large the hidl_vec
+ * needs to be. They resize the hidl_vec. Finally, it reruns the encodeHelper function which
+ * encodes T into the hidl_vec byte stream.
+ */
+template <class T>
+status_t encode(const T& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+    OutputHidlVec outputHidlVec{output};
+
+    status_t err = encodeHelper(input, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    err = outputHidlVec.resize();
+    if (err) {
+        return err;
+    }
+
+    return encodeHelper(input, &outputHidlVec);
+}
+
+template <class T>
+status_t encodeMetadata(const MetadataType& metadataType, const T& input, hidl_vec<uint8_t>* output,
+                EncodeHelper<T> encodeHelper) {
+    OutputHidlVec outputHidlVec{output};
+
+    status_t err = encodeMetadataType(metadataType, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    err = encodeHelper(input, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    err = outputHidlVec.resize();
+    if (err) {
+        return err;
+    }
+
+    err = encodeMetadataType(metadataType, &outputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    return encodeHelper(input, &outputHidlVec);
+}
+
+template <class T>
+status_t encodeOptionalMetadata(const MetadataType& metadataType, const std::optional<T>& input,
+                        hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+    if (!input) {
+        return NO_ERROR;
+    }
+    return encodeMetadata(metadataType, *input, output, encodeHelper);
+}
+
+/**
+ * decode/decodeMetadata are the main decoding functions. They take in a hidl_vec and use the
+ * decodeHelper function to turn the hidl_vec byte stream into T. If an error occurs, the
+ * errorHandler function cleans up T.
+ */
+template <class T>
+status_t decode(const hidl_vec<uint8_t>& input, T* output, DecodeHelper<T> decodeHelper,
+                ErrorHandler<T> errorHandler = nullptr) {
+    InputHidlVec inputHidlVec{&input};
+
+    status_t err = decodeHelper(&inputHidlVec, output);
+    if (err) {
+        return err;
+    }
+
+    err = inputHidlVec.hasRemainingData();
+    if (err) {
+        if (errorHandler) {
+            errorHandler(output);
+        }
+        return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
+template <class T>
+status_t decodeMetadata(const MetadataType& metadataType, const hidl_vec<uint8_t>& input, T* output,
+                DecodeHelper<T> decodeHelper, ErrorHandler<T> errorHandler = nullptr) {
+    InputHidlVec inputHidlVec{&input};
+
+    status_t err = validateMetadataType(&inputHidlVec, metadataType);
+    if (err) {
+        return err;
+    }
+
+    err = decodeHelper(&inputHidlVec, output);
+    if (err) {
+        return err;
+    }
+
+    err = inputHidlVec.hasRemainingData();
+    if (err) {
+        if (errorHandler) {
+            errorHandler(output);
+        }
+        return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
+template <class T>
+status_t decodeOptionalMetadata(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                        std::optional<T>* output, DecodeHelper<T> decodeHelper) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+    if (input.size() <= 0) {
+        output->reset();
+        return NO_ERROR;
+    }
+    T tmp;
+    status_t err = decodeMetadata(metadataType, input, &tmp, decodeHelper);
+    if (!err) {
+        *output = tmp;
+    }
+    return err;
+}
+
+/**
+ * Private helper functions
+ */
+template <class T>
+status_t encodeInteger(const T& input, OutputHidlVec* output) {
+    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
+    return output->encode(tmp, sizeof(input));
+}
+
+template <class T>
+status_t decodeInteger(InputHidlVec* input, T* output) {
+    static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, int32_t>::value ||
+                  std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+                  std::is_same<T, float>::value || std::is_same<T, double>::value);
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
+    return input->decode(tmp, sizeof(*output));
+}
+
+status_t encodeString(const std::string& input, OutputHidlVec* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = encodeInteger<int64_t>(input.size(), output);
+    if (err) {
+        return err;
+    }
+
+    return output->encode(reinterpret_cast<const uint8_t*>(input.data()), input.size());
+}
+
+status_t decodeString(InputHidlVec* input, std::string* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(input, &size);
+    if (err) {
+        return err;
+    }
+    if (size < 0) {
+        return BAD_VALUE;
+    }
+
+    return input->decode(output, size);
+}
+
+status_t encodeByteVector(const std::vector<uint8_t>& input, OutputHidlVec* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = encodeInteger<int64_t>(input.size(), output);
+    if (err) {
+        return err;
+    }
+
+    return output->encode(input.data(), input.size());
+}
+
+status_t decodeByteVector(InputHidlVec* input, std::vector<uint8_t>* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(input, &size);
+    if (err || size < 0) {
+        return err;
+    }
+
+    if (size > input->getRemainingSize()) {
+        return BAD_VALUE;
+    }
+    output->resize(size);
+
+    return input->decode(output->data(), size);
+}
+
+status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
+    status_t err = encodeString(input.name, output);
+    if (err) {
+        return err;
+    }
+
+    err = encodeInteger<int64_t>(input.value, output);
+    if (err) {
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+status_t decodeExtendableType(InputHidlVec* input, ExtendableType* output) {
+    status_t err = decodeString(input, &output->name);
+    if (err) {
+        return err;
+    }
+
+    err = decodeInteger<int64_t>(input, &output->value);
+    if (err) {
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+void clearExtendableType(ExtendableType* output) {
+    if (!output) {
+        return;
+    }
+    output->name.clear();
+    output->value = 0;
+}
+
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output) {
+    status_t err = encodeString(input.name, output);
+    if (err) {
+        return err;
+    }
+
+    err = encodeInteger<int64_t>(input.value, output);
+    if (err) {
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+status_t decodeMetadataType(InputHidlVec* input, MetadataType* output) {
+    std::string name;
+    status_t err = decodeString(input, &name);
+    if (err) {
+        return err;
+    }
+    output->name = name;
+
+    err = decodeInteger<int64_t>(input, &output->value);
+    if (err) {
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType) {
+    MetadataType receivedMetadataType;
+
+    status_t err = decodeMetadataType(input, &receivedMetadataType);
+    if (err) {
+        return err;
+    }
+
+    if (expectedMetadataType.name != receivedMetadataType.name) {
+        return BAD_VALUE;
+    }
+
+    if (receivedMetadataType.value != expectedMetadataType.value) {
+        return BAD_VALUE;
+    }
+
+    return NO_ERROR;
+}
+
+status_t encodeXyColor(const XyColor& input, OutputHidlVec* output) {
+    status_t err = encodeInteger<float>(input.x, output);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(input.y, output);
+}
+
+status_t decodeXyColor(InputHidlVec* input, XyColor* output) {
+    status_t err = decodeInteger<float>(input, &output->x);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(input, &output->y);
+}
+
+void clearXyColor(XyColor* output) {
+    if (!output) {
+        return;
+    }
+    output->x = 0;
+    output->y = 0;
+}
+
+status_t encodeRect(const Rect& input, OutputHidlVec* output) {
+    status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int32_t>(static_cast<int32_t>(input.top), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int32_t>(static_cast<int32_t>(input.right), output);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<int32_t>(static_cast<int32_t>(input.bottom), output);
+}
+
+status_t decodeRect(InputHidlVec* input, Rect* output) {
+    status_t err = decodeInteger<int32_t>(input, &output->left);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int32_t>(input, &output->top);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int32_t>(input, &output->right);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<int32_t>(input, &output->bottom);
+}
+
+status_t encodeBufferDescriptorInfoHelper(const BufferDescriptorInfo& input,
+        OutputHidlVec* output) {
+    status_t err = encodeString(input.name, output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<uint32_t>(input.width, output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<uint32_t>(input.height, output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<uint32_t>(input.layerCount, output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int32_t>(static_cast<int32_t>(input.format), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<uint64_t>(input.usage, output);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<uint64_t>(input.reservedSize, output);
+}
+
+status_t decodeBufferDescriptorInfoHelper(InputHidlVec* input, BufferDescriptorInfo* output) {
+    std::string name;
+    status_t err = decodeString(input, &name);
+    if (err) {
+        return err;
+    }
+    output->name = name;
+
+    err = decodeInteger<uint32_t>(input, &output->width);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<uint32_t>(input, &output->height);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<uint32_t>(input, &output->layerCount);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int32_t>(input, reinterpret_cast<int32_t*>(&output->format));
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<uint64_t>(input, &output->usage);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<uint64_t>(input, &output->reservedSize);
+}
+
+status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, OutputHidlVec* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = encodeExtendableType(input.type, output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int64_t>(static_cast<int64_t>(input.offsetInBits), output);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<int64_t>(static_cast<int64_t>(input.sizeInBits), output);
+}
+
+status_t decodePlaneLayoutComponent(InputHidlVec* input, PlaneLayoutComponent* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = decodeExtendableType(input, &output->type);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int64_t>(input, &output->offsetInBits);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<int64_t>(input, &output->sizeInBits);
+}
+
+status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, OutputHidlVec* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = encodeInteger<int64_t>(static_cast<int64_t>(input.size()), output);
+    if (err) {
+        return err;
+    }
+
+    for (const auto& planeLayoutComponent: input) {
+        err = encodePlaneLayoutComponent(planeLayoutComponent, output);
+        if (err) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t decodePlaneLayoutComponents(InputHidlVec* input, std::vector<PlaneLayoutComponent>* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(input, &size);
+    if (err) {
+        return err;
+    }
+    if (size < 0 || size > 10000) {
+        return BAD_VALUE;
+    }
+
+    output->resize(size);
+
+    for (auto& planeLayoutComponent : *output) {
+        err = decodePlaneLayoutComponent(input, &planeLayoutComponent);
+        if (err) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t encodePlaneLayout(const PlaneLayout& input, OutputHidlVec* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = encodePlaneLayoutComponents(input.components, output);
+    if (err) {
+        return err;
+    }
+
+    err = encodeInteger<int64_t>(static_cast<int32_t>(input.offsetInBytes), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int64_t>(static_cast<int32_t>(input.sampleIncrementInBits), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int64_t>(static_cast<int32_t>(input.strideInBytes), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int64_t>(static_cast<int32_t>(input.widthInSamples), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int64_t>(static_cast<int32_t>(input.heightInSamples), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int64_t>(static_cast<int32_t>(input.totalSizeInBytes), output);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<int64_t>(static_cast<int32_t>(input.horizontalSubsampling), output);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
+}
+
+status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
+    if (!output) {
+        return BAD_VALUE;
+    }
+
+    status_t err = decodePlaneLayoutComponents(input, &output->components);
+    if (err) {
+        return err;
+    }
+
+    err = decodeInteger<int64_t>(input, &output->offsetInBytes);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int64_t>(input, &output->strideInBytes);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int64_t>(input, &output->widthInSamples);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int64_t>(input, &output->heightInSamples);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int64_t>(input, &output->totalSizeInBytes);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<int64_t>(input, &output->horizontalSubsampling);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<int64_t>(input, &output->verticalSubsampling);
+}
+
+status_t encodePlaneLayoutsHelper(const std::vector<PlaneLayout>& planeLayouts, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    for (const auto& planeLayout : planeLayouts) {
+        err = encodePlaneLayout(planeLayout, outOutputHidlVec);
+        if (err) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t decodePlaneLayoutsHelper(InputHidlVec* inputHidlVec, std::vector<PlaneLayout>* outPlaneLayouts) {
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
+    if (err) {
+        return err;
+    }
+    if (size < 0) {
+        return BAD_VALUE;
+    }
+
+    for (size_t i = 0; i < size; i++) {
+        outPlaneLayouts->emplace_back();
+        err = decodePlaneLayout(inputHidlVec, &outPlaneLayouts->back());
+        if (err) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+void clearPlaneLayouts(std::vector<PlaneLayout>* output) {
+    if (!output) {
+        return;
+    }
+    output->clear();
+}
+
+status_t encodeCropHelper(const std::vector<Rect>& crops, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeInteger<int64_t>(static_cast<int64_t>(crops.size()), outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+
+    for (const auto& crop : crops) {
+        err = encodeRect(crop, outOutputHidlVec);
+        if (err) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t decodeCropHelper(InputHidlVec* inputHidlVec, std::vector<Rect>* outCrops) {
+    int64_t size = 0;
+    status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
+    if (err) {
+        return err;
+    }
+    if (size < 0) {
+        return BAD_VALUE;
+    }
+
+    for (size_t i = 0; i < size; i++) {
+        outCrops->emplace_back();
+        err = decodeRect(inputHidlVec, &outCrops->back());
+        if (err) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+void clearCrop(std::vector<Rect>* output) {
+    if (!output) {
+        return;
+    }
+    output->clear();
+}
+
+status_t encodeSmpte2086Helper(const Smpte2086& smpte2086, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeXyColor(smpte2086.primaryRed, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.primaryGreen, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.primaryBlue, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeXyColor(smpte2086.whitePoint, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    err = encodeInteger<float>(smpte2086.maxLuminance, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(smpte2086.minLuminance, outOutputHidlVec);
+}
+
+status_t decodeSmpte2086Helper(InputHidlVec* inputHidlVec, Smpte2086* outSmpte2086) {
+    status_t err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryRed);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryGreen);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryBlue);
+    if (err) {
+        return err;
+    }
+    err = decodeXyColor(inputHidlVec, &outSmpte2086->whitePoint);
+    if (err) {
+        return err;
+    }
+    err = decodeInteger<float>(inputHidlVec, &outSmpte2086->maxLuminance);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(inputHidlVec, &outSmpte2086->minLuminance);
+}
+
+status_t encodeCta861_3Helper(const Cta861_3& cta861_3, OutputHidlVec* outOutputHidlVec) {
+    status_t err = encodeInteger<float>(cta861_3.maxContentLightLevel, outOutputHidlVec);
+    if (err) {
+        return err;
+    }
+    return encodeInteger<float>(cta861_3.maxFrameAverageLightLevel, outOutputHidlVec);
+}
+
+status_t decodeCta861_3Helper(InputHidlVec* inputHidlVec, Cta861_3* outCta861_3) {
+    status_t err = decodeInteger<float>(inputHidlVec, &outCta861_3->maxContentLightLevel);
+    if (err) {
+        return err;
+    }
+    return decodeInteger<float>(inputHidlVec, &outCta861_3->maxFrameAverageLightLevel);
+}
+
+/**
+ * Public API functions
+ */
+status_t encodeBufferDescriptorInfo(const BufferDescriptorInfo& bufferDescriptorInfo,
+        hidl_vec<uint8_t>* outBufferDescriptorInfo) {
+    return encode(bufferDescriptorInfo, outBufferDescriptorInfo, encodeBufferDescriptorInfoHelper);
+}
+
+status_t decodeBufferDescriptorInfo(const hidl_vec<uint8_t>& bufferDescriptorInfo,
+        BufferDescriptorInfo* outBufferDescriptorInfo) {
+    return decode(bufferDescriptorInfo, outBufferDescriptorInfo, decodeBufferDescriptorInfoHelper);
+}
+
+status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
+    return encodeMetadata(MetadataType_BufferId, bufferId, outBufferId, encodeInteger);
+}
+
+status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
+    return decodeMetadata(MetadataType_BufferId, bufferId, outBufferId, decodeInteger);
+}
+
+status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
+    return encodeMetadata(MetadataType_Name, name, outName, encodeString);
+}
+
+status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
+    return decodeMetadata(MetadataType_Name, name, outName, decodeString);
+}
+
+status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
+    return encodeMetadata(MetadataType_Width, width, outWidth, encodeInteger);
+}
+
+status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
+    return decodeMetadata(MetadataType_Width, width, outWidth, decodeInteger);
+}
+
+status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
+    return encodeMetadata(MetadataType_Height, height, outHeight, encodeInteger);
+}
+
+status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
+    return decodeMetadata(MetadataType_Height, height, outHeight, decodeInteger);
+}
+
+status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
+    return encodeMetadata(MetadataType_LayerCount, layerCount, outLayerCount, encodeInteger);
+}
+
+status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
+    return decodeMetadata(MetadataType_LayerCount, layerCount, outLayerCount, decodeInteger);
+}
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
+        hidl_vec<uint8_t>* outPixelFormatRequested) {
+    return encodeMetadata(MetadataType_PixelFormatRequested, static_cast<int32_t>(pixelFormatRequested),
+                  outPixelFormatRequested, encodeInteger);
+}
+
+status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
+        hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
+    return decodeMetadata(MetadataType_PixelFormatRequested, pixelFormatRequested,
+                  reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
+}
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
+    return encodeMetadata(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+                  encodeInteger);
+}
+
+status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
+    return decodeMetadata(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+                  decodeInteger);
+}
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
+    return encodeMetadata(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+                  encodeInteger);
+}
+
+status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
+    return decodeMetadata(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+                  decodeInteger);
+}
+
+status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
+    return encodeMetadata(MetadataType_Usage, usage, outUsage, encodeInteger);
+}
+
+status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
+    return decodeMetadata(MetadataType_Usage, usage, outUsage, decodeInteger);
+}
+
+status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
+    return encodeMetadata(MetadataType_AllocationSize, allocationSize, outAllocationSize, encodeInteger);
+}
+
+status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
+    return decodeMetadata(MetadataType_AllocationSize, allocationSize, outAllocationSize, decodeInteger);
+}
+
+status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
+    return encodeMetadata(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+                  encodeInteger);
+}
+
+status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
+    return decodeMetadata(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+                  decodeInteger);
+}
+
+status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
+    return encodeMetadata(MetadataType_Compression, compression, outCompression, encodeExtendableType);
+}
+
+status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
+    return decodeMetadata(MetadataType_Compression, compression, outCompression, decodeExtendableType,
+                  clearExtendableType);
+}
+
+status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
+    return encodeMetadata(MetadataType_Interlaced, interlaced, outInterlaced, encodeExtendableType);
+}
+
+status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
+    return decodeMetadata(MetadataType_Interlaced, interlaced, outInterlaced, decodeExtendableType,
+                  clearExtendableType);
+}
+
+status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
+    return encodeMetadata(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, encodeExtendableType);
+}
+
+status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
+    return decodeMetadata(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, decodeExtendableType,
+                  clearExtendableType);
+}
+
+status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
+    return encodeMetadata(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+                  encodePlaneLayoutsHelper);
+}
+
+status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
+    return decodeMetadata(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+                  decodePlaneLayoutsHelper, clearPlaneLayouts);
+}
+
+status_t encodeCrop(const std::vector<Rect>& crop, hidl_vec<uint8_t>* outCrop) {
+    return encodeMetadata(MetadataType_Crop, crop, outCrop, encodeCropHelper);
+}
+
+status_t decodeCrop(const hidl_vec<uint8_t>& crop, std::vector<Rect>* outCrop) {
+    return decodeMetadata(MetadataType_Crop, crop, outCrop, decodeCropHelper, clearCrop);
+}
+
+status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
+    return encodeMetadata(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
+                  encodeInteger);
+}
+
+status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
+    return decodeMetadata(MetadataType_Dataspace, dataspace, reinterpret_cast<int32_t*>(outDataspace),
+                  decodeInteger);
+}
+
+status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
+    return encodeMetadata(MetadataType_BlendMode, static_cast<int32_t>(blendMode), outBlendMode,
+                  encodeInteger);
+}
+
+status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
+    return decodeMetadata(MetadataType_BlendMode, blendMode, reinterpret_cast<int32_t*>(outBlendMode),
+                  decodeInteger);
+}
+
+status_t encodeSmpte2086(const std::optional<Smpte2086>& smpte2086,
+                         hidl_vec<uint8_t>* outSmpte2086) {
+    return encodeOptionalMetadata(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
+}
+
+status_t decodeSmpte2086(const hidl_vec<uint8_t>& smpte2086,
+                         std::optional<Smpte2086>* outSmpte2086) {
+    return decodeOptionalMetadata(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
+}
+
+status_t encodeCta861_3(const std::optional<Cta861_3>& cta861_3, hidl_vec<uint8_t>* outCta861_3) {
+    return encodeOptionalMetadata(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
+}
+
+status_t decodeCta861_3(const hidl_vec<uint8_t>& cta861_3, std::optional<Cta861_3>* outCta861_3) {
+    return decodeOptionalMetadata(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
+}
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+                            hidl_vec<uint8_t>* outSmpte2094_40) {
+    return encodeOptionalMetadata(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+                          encodeByteVector);
+}
+
+status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+    return decodeOptionalMetadata(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+                          decodeByteVector);
+}
+
+status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
+                      hidl_vec<uint8_t>* output) {
+    return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      uint32_t* output) {
+    return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt32(const MetadataType& metadataType, int32_t input,
+                     hidl_vec<uint8_t>* output) {
+    return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                     int32_t* output) {
+    return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeUint64(const MetadataType& metadataType, uint64_t input,
+                      hidl_vec<uint8_t>* output) {
+    return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      uint64_t* output) {
+    return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt64(const MetadataType& metadataType, int64_t input,
+                     hidl_vec<uint8_t>* output) {
+    return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                     int64_t* output) {
+    return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeFloat(const MetadataType& metadataType, float input,
+                     hidl_vec<uint8_t>* output) {
+    return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeFloat(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                     float* output) {
+    return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeDouble(const MetadataType& metadataType, double input,
+                      hidl_vec<uint8_t>* output) {
+    return encodeMetadata(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeDouble(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      double* output) {
+    return decodeMetadata(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeString(const MetadataType& metadataType, const std::string& input,
+                      hidl_vec<uint8_t>* output) {
+    return encodeMetadata(metadataType, input, output, encodeString);
+}
+
+status_t decodeString(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+                      std::string* output) {
+    return decodeMetadata(metadataType, input, output, decodeString);
+}
+
+bool isStandardMetadataType(const MetadataType& metadataType) {
+    return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE,
+                         metadataType.name.size());
+}
+
+bool isStandardCompression(const ExtendableType& compression) {
+    return !std::strncmp(compression.name.c_str(), GRALLOC4_STANDARD_COMPRESSION,
+                         compression.name.size());
+}
+
+bool isStandardInterlaced(const ExtendableType& interlaced) {
+    return !std::strncmp(interlaced.name.c_str(), GRALLOC4_STANDARD_INTERLACED,
+                         interlaced.name.size());
+}
+
+bool isStandardChromaSiting(const ExtendableType& chromaSiting) {
+    return !std::strncmp(chromaSiting.name.c_str(), GRALLOC4_STANDARD_CHROMA_SITING,
+                         chromaSiting.name.size());
+}
+
+bool isStandardPlaneLayoutComponentType(const ExtendableType& planeLayoutComponentType) {
+    return !std::strncmp(planeLayoutComponentType.name.c_str(), GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+                         planeLayoutComponentType.name.size());
+}
+
+StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
+    return static_cast<StandardMetadataType>(metadataType.value);
+}
+
+Compression getStandardCompressionValue(const ExtendableType& compression) {
+    return static_cast<Compression>(compression.value);
+}
+
+Interlaced getStandardInterlacedValue(const ExtendableType& interlaced) {
+    return static_cast<Interlaced>(interlaced.value);
+}
+
+ChromaSiting getStandardChromaSitingValue(const ExtendableType& chromaSiting) {
+    return static_cast<ChromaSiting>(chromaSiting.value);
+}
+
+PlaneLayoutComponentType getStandardPlaneLayoutComponentTypeValue(
+        const ExtendableType& planeLayoutComponentType) {
+    return static_cast<PlaneLayoutComponentType>(planeLayoutComponentType.value);
+}
+
+std::string getCompressionName(const ExtendableType& compression) {
+    if (!isStandardCompression(compression)) {
+        std::ostringstream stream;
+        stream << compression.name << "#" << compression.value;
+        return stream.str();
+    }
+    switch (getStandardCompressionValue(compression)) {
+        case Compression::NONE:
+            return "None";
+        case Compression::DISPLAY_STREAM_COMPRESSION:
+            return "DisplayStreamCompression";
+    }
+}
+
+std::string getInterlacedName(const ExtendableType& interlaced) {
+    if (!isStandardInterlaced(interlaced)) {
+        std::ostringstream stream;
+        stream << interlaced.name << "#" << interlaced.value;
+        return stream.str();
+    }
+    switch (getStandardInterlacedValue(interlaced)) {
+        case Interlaced::NONE:
+            return "None";
+        case Interlaced::TOP_BOTTOM:
+            return "TopBottom";
+        case Interlaced::RIGHT_LEFT:
+            return "RightLeft";
+    }
+}
+
+std::string getChromaSitingName(const ExtendableType& chromaSiting) {
+    if (!isStandardChromaSiting(chromaSiting)) {
+        std::ostringstream stream;
+        stream << chromaSiting.name << "#" << chromaSiting.value;
+        return stream.str();
+    }
+    switch (getStandardChromaSitingValue(chromaSiting)) {
+        case ChromaSiting::NONE:
+            return "None";
+        case ChromaSiting::UNKNOWN:
+            return "Unknown";
+        case ChromaSiting::SITED_INTERSTITIAL:
+            return "SitedInterstitial";
+        case ChromaSiting::COSITED_HORIZONTAL:
+            return "CositedHorizontal";
+    }
+}
+
+std::string getPlaneLayoutComponentTypeName(const ExtendableType& planeLayoutComponentType) {
+    if (!isStandardPlaneLayoutComponentType(planeLayoutComponentType)) {
+        std::ostringstream stream;
+        stream << planeLayoutComponentType.name << "#" << planeLayoutComponentType.value;
+        return stream.str();
+    }
+    switch (getStandardPlaneLayoutComponentTypeValue(planeLayoutComponentType)) {
+        case PlaneLayoutComponentType::Y:
+            return "Y";
+        case PlaneLayoutComponentType::CB:
+            return "Cb";
+        case PlaneLayoutComponentType::CR:
+            return "Cr";
+        case PlaneLayoutComponentType::R:
+            return "R";
+        case PlaneLayoutComponentType::G:
+            return "G";
+        case PlaneLayoutComponentType::B:
+            return "B";
+        case PlaneLayoutComponentType::A:
+            return "A";
+    }
+}
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
new file mode 100644
index 0000000..8444883
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -0,0 +1,32 @@
+cc_fuzz {
+    name: "libgralloctypes_fuzzer",
+    defaults: ["libbinder_ndk_host_user"],
+    host_supported: true,
+
+    fuzz_config: {
+        cc: ["marissaw@google.com"],
+    },
+
+    srcs: [
+        "gralloctypes.cpp",
+        "main.cpp",
+        "util.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libcgrouprc",
+        "libcgrouprc_format",
+        "libcutils",
+        "libgralloctypes",
+        "libhidlbase",
+        "liblog",
+        "libprocessgroup",
+        "libjsoncpp",
+        "libutils",
+    ],
+
+    // This flag enables verbose output in the fuzz target, and is very useful
+    // for debugging a failure. If you are trying to diagnose how a crash was
+    // produced, you may find uncommenting the below line very useful.
+    // cflags: ["-DENABLE_LOG_FUZZ"],
+}
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
new file mode 100644
index 0000000..dc22385
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 FUZZ_LOG_TAG "gralloctypes"
+
+#include <cstdint>
+
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+#include <utils/Errors.h>
+
+#include "gralloctypes.h"
+#include "util.h"
+
+using ::android::status_t;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+
+#define GRALLOCTYPES_DECODE(T, FUNC) \
+    [] (const ::android::hardware::hidl_vec<uint8_t>& vec) {\
+        FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+        T t;\
+        status_t err = FUNC(vec, &t);\
+        (void) err;\
+        FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+    }
+
+#define GRALLOCTYPES_DECODE_VENDOR_HELPER(T, FUNC) \
+    [] (const MetadataType& metadataType, const ::android::hardware::hidl_vec<uint8_t>& vec) {\
+        FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+        T t;\
+        status_t err = FUNC(metadataType, vec, &t);\
+        (void) err;\
+        FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+    }
+
+
+// clang-format off
+std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
+    GRALLOCTYPES_DECODE(BufferDescriptorInfo, ::android::gralloc4::decodeBufferDescriptorInfo),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeBufferId),
+    GRALLOCTYPES_DECODE(std::string, ::android::gralloc4::decodeName),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeWidth),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeHeight),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeLayerCount),
+    GRALLOCTYPES_DECODE(::android::hardware::graphics::common::V1_2::PixelFormat, ::android::gralloc4::decodePixelFormatRequested),
+    GRALLOCTYPES_DECODE(uint32_t, ::android::gralloc4::decodePixelFormatFourCC),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodePixelFormatModifier),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeUsage),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeAllocationSize),
+    GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeProtectedContent),
+    GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeCompression),
+    GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeInterlaced),
+    GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeChromaSiting),
+    GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
+    GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::Rect>, ::android::gralloc4::decodeCrop),
+    GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
+    GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
+    GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Smpte2086>, ::android::gralloc4::decodeSmpte2086),
+    GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Cta861_3>, ::android::gralloc4::decodeCta861_3),
+    GRALLOCTYPES_DECODE(std::optional<std::vector<uint8_t>>, ::android::gralloc4::decodeSmpte2094_40),
+};
+
+std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS {
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(uint32_t, ::android::gralloc4::decodeUint32),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(int32_t, ::android::gralloc4::decodeInt32),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(uint64_t, ::android::gralloc4::decodeUint64),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(int64_t, ::android::gralloc4::decodeInt64),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(float, ::android::gralloc4::decodeFloat),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(double, ::android::gralloc4::decodeDouble),
+    GRALLOCTYPES_DECODE_VENDOR_HELPER(std::string, ::android::gralloc4::decodeString),
+};
+// clang-format on
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.h b/libs/gralloc/types/fuzzer/gralloctypes.h
new file mode 100644
index 0000000..a3fe2d9
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+
+#include <vector>
+
+using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec)>;
+using GrallocTypesVendorHelperDecode = std::function<void(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType&, const ::android::hardware::hidl_vec<uint8_t>& vec)>;
+
+extern std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS;
+extern std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS;
diff --git a/libs/gralloc/types/fuzzer/main.cpp b/libs/gralloc/types/fuzzer/main.cpp
new file mode 100644
index 0000000..8779fa2
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/main.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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 FUZZ_LOG_TAG "main"
+
+#include "gralloctypes.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <log/log.h>
+
+#include <cstdlib>
+#include <ctime>
+
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+void doFuzz(
+        const std::vector<GrallocTypesDecode>& decodes, uint8_t instruction,
+        const std::vector<uint8_t>& input) {
+
+    ::android::hardware::hidl_vec<uint8_t> vec;
+    vec.setToExternal(const_cast<uint8_t*>(input.data()), input.size(), false /*shouldOwn*/);
+
+    // since we are only using a byte to index
+    CHECK(decodes.size() <= 255) << decodes.size();
+    uint8_t decodeIdx = instruction % decodes.size();
+
+    FUZZ_LOG() << "Instruction: " << instruction << " idx: " << static_cast<size_t>(decodeIdx)
+               << " size: " << vec.size();
+
+    decodes[decodeIdx](vec);
+}
+
+size_t fillInMetadataType(const std::vector<uint8_t>& input, MetadataType* outMetadataType) {
+    if (input.size() < sizeof(outMetadataType->value) + 1) {
+        return 0;
+    }
+    size_t size = 0;
+
+    outMetadataType->value = *(reinterpret_cast<const int64_t*>(input.data()));
+    size += sizeof(outMetadataType->value);
+
+    uint8_t nameLen = *(input.data() + size);
+    size += 1;
+
+    if (input.size() < size + nameLen) {
+        return 0;
+    }
+    std::string name(reinterpret_cast<const char*>(input.data()) + size, nameLen);
+    outMetadataType->name = name;
+    return size + nameLen;
+}
+
+void doFuzzVendorHelper(
+        const std::vector<GrallocTypesVendorHelperDecode>& decodes, uint8_t instruction,
+        const std::vector<uint8_t>& input) {
+
+    MetadataType metadataType;
+    size_t sizeUsed  = fillInMetadataType(input, &metadataType);
+    if (sizeUsed <= 0) {
+        return;
+    }
+
+    ::android::hardware::hidl_vec<uint8_t> vec;
+    vec.setToExternal(const_cast<uint8_t*>(input.data() + sizeUsed), input.size() - sizeUsed,
+                      false /*shouldOwn*/);
+
+    // since we are only using a byte to index
+    CHECK(decodes.size() <= 255) << decodes.size();
+    uint8_t decodeIdx = instruction % decodes.size();
+
+    FUZZ_LOG() << "Vendor Helper instruction: " << instruction << " idx: "
+               << static_cast<size_t>(decodeIdx) << " size: " << vec.size();
+
+    decodes[decodeIdx](metadataType, vec);
+}
+
+void fuzz(uint8_t options, uint8_t instruction, const std::vector<uint8_t>& input) {
+    uint8_t option = options & 0x1;
+
+    switch (option) {
+        case 0x0:
+            doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, instruction, input);
+            break;
+        case 0x1:
+            doFuzzVendorHelper(GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS, instruction, input);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("unknown gralloc types %d", static_cast<int>(option));
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size <= 1) return 0;  // no use
+
+    uint8_t options = *data;
+    data++;
+    size--;
+
+    uint8_t instruction = *data;
+    data++;
+    size--;
+
+    std::vector<uint8_t> input(data, data + size);
+
+    FUZZ_LOG() << "input: " << hexString(input);
+
+    fuzz(options, instruction, input);
+    return 0;
+}
diff --git a/libs/gralloc/types/fuzzer/util.cpp b/libs/gralloc/types/fuzzer/util.cpp
new file mode 100644
index 0000000..479f406
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.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.
+ */
+#define FUZZ_LOG_TAG "util"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <iomanip>
+#include <sstream>
+
+std::string hexString(const void* bytes, size_t len) {
+    if (bytes == nullptr) return "<null>";
+
+    const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+    char chars[] = "0123456789abcdef";
+    std::string result;
+    result.resize(len * 2);
+
+    for (size_t i = 0; i < len; i++) {
+        result[2 * i] = chars[bytes8[i] >> 4];
+        result[2 * i + 1] = chars[bytes8[i] & 0xf];
+    }
+
+    return result;
+}
+std::string hexString(const std::vector<uint8_t>& bytes) {
+    return hexString(bytes.data(), bytes.size());
+}
diff --git a/libs/gralloc/types/fuzzer/util.h b/libs/gralloc/types/fuzzer/util.h
new file mode 100644
index 0000000..aa504d2
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.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.
+ */
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifndef FUZZ_LOG_TAG
+#error "Must define FUZZ_LOG_TAG"
+#endif
+
+#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG).log()
+
+#ifdef ENABLE_LOG_FUZZ
+class FuzzLog {
+public:
+    FuzzLog(const char* tag) : mTag(tag) {}
+    ~FuzzLog() { std::cout << mTag << ": " << mOs.str() << std::endl; }
+
+    std::stringstream& log() { return mOs; }
+
+private:
+    const char* mTag = nullptr;
+    std::stringstream mOs;
+};
+#else
+class FuzzLog {
+public:
+    FuzzLog(const char* /*tag*/) {}
+    template <typename T>
+    FuzzLog& operator<<(const T& /*t*/) {
+        return *this;
+    }
+    FuzzLog& log() { return *this; }
+};
+#endif
+
+std::string hexString(const void* bytes, size_t len);
+std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
new file mode 100644
index 0000000..5ec4d0d
--- /dev/null
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -0,0 +1,611 @@
+/*
+ * 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 <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/XyColor.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+
+namespace android {
+
+/**
+ * Define equality operators for Stable AIDL types.
+ */
+inline bool operator==(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+                const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+    return !std::strcmp(lhs.name.c_str(), rhs.name.c_str()) && lhs.value == rhs.value;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+                const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+    if (lhs.type.name != rhs.type.name) {
+        return false;
+    }
+    if (lhs.type.value != rhs.type.value) {
+        return false;
+    }
+    if (lhs.sizeInBits != rhs.sizeInBits) {
+        return false;
+    }
+    if (lhs.offsetInBits != rhs.offsetInBits) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Rect& lhs,
+                const aidl::android::hardware::graphics::common::Rect& rhs) {
+    if (lhs.left != rhs.left) {
+        return false;
+    }
+    if (lhs.top != rhs.top) {
+        return false;
+    }
+    if (lhs.right != rhs.right) {
+        return false;
+    }
+    if (lhs.bottom != rhs.bottom) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Rect& lhs,
+                const aidl::android::hardware::graphics::common::Rect& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
+    if (lhs.size() != rhs.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < lhs.size(); i++) {
+        if (lhs[i] != rhs[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::Rect>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::Rect>& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+    if (lhs.offsetInBytes != rhs.offsetInBytes) {
+        return false;
+    }
+    if (lhs.sampleIncrementInBits != rhs.sampleIncrementInBits) {
+        return false;
+    }
+    if (lhs.strideInBytes != rhs.strideInBytes) {
+        return false;
+    }
+    if (lhs.widthInSamples != rhs.widthInSamples) {
+        return false;
+    }
+    if (lhs.heightInSamples != rhs.heightInSamples) {
+        return false;
+    }
+    if (lhs.totalSizeInBytes != rhs.totalSizeInBytes) {
+        return false;
+    }
+    if (lhs.horizontalSubsampling != rhs.horizontalSubsampling) {
+        return false;
+    }
+    if (lhs.verticalSubsampling != rhs.verticalSubsampling) {
+        return false;
+    }
+    if (lhs.components.size() != rhs.components.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < lhs.components.size(); i++) {
+        if (lhs.components[i] != rhs.components[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+                const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+    if (lhs.size() != rhs.size()) {
+        return false;
+    }
+    for (size_t i = 0; i < lhs.size(); i++) {
+        if (lhs[i] != rhs[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+                const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::XyColor& lhs,
+                const aidl::android::hardware::graphics::common::XyColor& rhs) {
+    if (lhs.x != rhs.x) {
+        return false;
+    }
+    if (lhs.y != rhs.y) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::XyColor& lhs,
+                const aidl::android::hardware::graphics::common::XyColor& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+                const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+    if (lhs.primaryRed != rhs.primaryRed) {
+        return false;
+    }
+    if (lhs.primaryGreen != rhs.primaryGreen) {
+        return false;
+    }
+    if (lhs.primaryBlue != rhs.primaryBlue) {
+        return false;
+    }
+    if (lhs.whitePoint != rhs.whitePoint) {
+        return false;
+    }
+    if (lhs.maxLuminance != rhs.maxLuminance) {
+        return false;
+    }
+    if (lhs.minLuminance != rhs.minLuminance) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+                const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+    return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+                const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+    if (lhs.maxContentLightLevel != rhs.maxContentLightLevel) {
+        return false;
+    }
+    if (lhs.maxFrameAverageLightLevel != rhs.maxFrameAverageLightLevel) {
+        return false;
+    }
+    return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+                const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+    return !(lhs == rhs);
+}
+
+namespace gralloc4 {
+
+#define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
+#define GRALLOC4_STANDARD_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
+#define GRALLOC4_STANDARD_COMPRESSION "android.hardware.graphics.common.Compression"
+#define GRALLOC4_STANDARD_INTERLACED "android.hardware.graphics.common.Interlaced"
+#define GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE \
+    "android.hardware.graphics.common.PlaneLayoutComponentType"
+
+/*---------------------------------------------------------------------------------------------*/
+/**
+ * Definitions of the standard buffer metadata types. It is recommended that everyone uses
+ * these definitions directly for standard buffer metadata types.
+ */
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BufferId = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BUFFER_ID)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Name = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::NAME)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Width = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::WIDTH)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Height = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::HEIGHT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_LayerCount = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::LAYER_COUNT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatRequested = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_REQUESTED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatFourCC = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_FOURCC)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatModifier = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_MODIFIER)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Usage = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::USAGE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_AllocationSize = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::ALLOCATION_SIZE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ProtectedContent = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PROTECTED_CONTENT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Compression = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::COMPRESSION)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Interlaced = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::INTERLACED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ChromaSiting = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::CHROMA_SITING)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PlaneLayouts = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PLANE_LAYOUTS)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Crop = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::CROP)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Dataspace = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::DATASPACE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BlendMode = {
+        GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2086 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                  static_cast<int64_t>(aidl::android::hardware::graphics::common::
+                                                               StandardMetadataType::SMPTE2086)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Cta861_3 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                 static_cast<int64_t>(aidl::android::hardware::graphics::common::
+                                                              StandardMetadataType::CTA861_3)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+        MetadataType_Smpte2094_40 = {GRALLOC4_STANDARD_METADATA_TYPE,
+                                     static_cast<int64_t>(
+                                             aidl::android::hardware::graphics::common::
+                                                     StandardMetadataType::SMPTE2094_40)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard compression strategies. It is recommended that everyone uses
+ * these definitions directly for standard compression strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_None =
+        {GRALLOC4_STANDARD_COMPRESSION,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType
+        Compression_DisplayStreamCompression =
+                {GRALLOC4_STANDARD_COMPRESSION,
+                 static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::
+                                              DISPLAY_STREAM_COMPRESSION)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard interlaced strategies. It is recommended that everyone uses
+ * these definitions directly for standard interlaced strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None =
+        {GRALLOC4_STANDARD_INTERLACED,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom =
+        {GRALLOC4_STANDARD_INTERLACED,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft =
+        {GRALLOC4_STANDARD_INTERLACED,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard chroma siting. It is recommended that everyone uses
+ * these definitions directly for standard chroma siting.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None =
+        {GRALLOC4_STANDARD_CHROMA_SITING,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown =
+        {GRALLOC4_STANDARD_CHROMA_SITING,
+         static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType
+        ChromaSiting_SitedInterstitial = {GRALLOC4_STANDARD_CHROMA_SITING,
+                                          static_cast<int64_t>(
+                                                  aidl::android::hardware::graphics::common::
+                                                          ChromaSiting::SITED_INTERSTITIAL)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType
+        ChromaSiting_CositedHorizontal = {GRALLOC4_STANDARD_CHROMA_SITING,
+                                          static_cast<int64_t>(
+                                                  aidl::android::hardware::graphics::common::
+                                                          ChromaSiting::COSITED_HORIZONTAL)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard plane layout component types. It is recommended that everyone uses
+ * these definitions directly for standard plane layout component types
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A =
+        {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+         static_cast<int64_t>(
+                 aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * The functions below encode and decode BufferDescriptorInfo into a byte stream.
+ */
+status_t encodeBufferDescriptorInfo(const android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo& bufferDescriptorInfo, android::hardware::hidl_vec<uint8_t>* outBufferDescriptorInfo);
+status_t decodeBufferDescriptorInfo(const android::hardware::hidl_vec<uint8_t>& bufferDescriptorInfo, android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* outBufferDescriptorInfo);
+
+/**
+ * The functions below encode and decode standard metadata into a byte stream. It is STRONGLY
+ * recommended that both the vendor and system partitions use these functions when getting
+ * and setting metadata through gralloc 4 (IMapper 4.0).
+ */
+status_t encodeBufferId(uint64_t bufferId, android::hardware::hidl_vec<uint8_t>* outBufferId);
+status_t decodeBufferId(const android::hardware::hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId);
+
+status_t encodeName(const std::string& name, android::hardware::hidl_vec<uint8_t>* outName);
+status_t decodeName(const android::hardware::hidl_vec<uint8_t>& name, std::string* outName);
+
+status_t encodeWidth(uint64_t width, android::hardware::hidl_vec<uint8_t>* outWidth);
+status_t decodeWidth(const android::hardware::hidl_vec<uint8_t>& width, uint64_t* outWidth);
+
+status_t encodeHeight(uint64_t height, android::hardware::hidl_vec<uint8_t>* outHeight);
+status_t decodeHeight(const android::hardware::hidl_vec<uint8_t>& height, uint64_t* outHeight);
+
+status_t encodeLayerCount(uint64_t layerCount, android::hardware::hidl_vec<uint8_t>* outLayerCount);
+status_t decodeLayerCount(const android::hardware::hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount);
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested, android::hardware::hidl_vec<uint8_t>* outPixelFormatRequested);
+status_t decodePixelFormatRequested(const android::hardware::hidl_vec<uint8_t>& pixelFormatRequested, hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested);
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, android::hardware::hidl_vec<uint8_t>* outPixelFormatFourCC);
+status_t decodePixelFormatFourCC(const android::hardware::hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC);
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, android::hardware::hidl_vec<uint8_t>* outPixelFormatModifier);
+status_t decodePixelFormatModifier(const android::hardware::hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier);
+
+status_t encodeUsage(uint64_t usage, android::hardware::hidl_vec<uint8_t>* outUsage);
+status_t decodeUsage(const android::hardware::hidl_vec<uint8_t>& usage, uint64_t* outUsage);
+
+status_t encodeAllocationSize(uint64_t allocationSize, android::hardware::hidl_vec<uint8_t>* outAllocationSize);
+status_t decodeAllocationSize(const android::hardware::hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize);
+
+status_t encodeProtectedContent(uint64_t protectedContent, android::hardware::hidl_vec<uint8_t>* outProtectedContent);
+status_t decodeProtectedContent(const android::hardware::hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent);
+
+status_t encodeCompression(const aidl::android::hardware::graphics::common::ExtendableType& compression, android::hardware::hidl_vec<uint8_t>* outCompression);
+status_t decodeCompression(const android::hardware::hidl_vec<uint8_t>& compression, aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+
+status_t encodeInterlaced(const aidl::android::hardware::graphics::common::ExtendableType& interlaced, android::hardware::hidl_vec<uint8_t>* outInterlaced);
+status_t decodeInterlaced(const android::hardware::hidl_vec<uint8_t>& interlaced, aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+
+status_t encodeChromaSiting(const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting, android::hardware::hidl_vec<uint8_t>* outChromaSiting);
+status_t decodeChromaSiting(const android::hardware::hidl_vec<uint8_t>& chromaSiting, aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+
+status_t encodePlaneLayouts(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& planeLayouts, android::hardware::hidl_vec<uint8_t>* outPlaneLayouts);
+status_t decodePlaneLayouts(const android::hardware::hidl_vec<uint8_t>& planeLayouts, std::vector<aidl::android::hardware::graphics::common::PlaneLayout>* outPlaneLayouts);
+
+status_t encodeCrop(const std::vector<aidl::android::hardware::graphics::common::Rect>& crop, android::hardware::hidl_vec<uint8_t>* outCrop);
+status_t decodeCrop(const android::hardware::hidl_vec<uint8_t>& crop, std::vector<aidl::android::hardware::graphics::common::Rect>* outCrop);
+
+status_t encodeDataspace(const aidl::android::hardware::graphics::common::Dataspace& dataspace, android::hardware::hidl_vec<uint8_t>* outDataspace);
+status_t decodeDataspace(const android::hardware::hidl_vec<uint8_t>& dataspace, aidl::android::hardware::graphics::common::Dataspace* outDataspace);
+
+status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
+status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
+
+status_t encodeSmpte2086(
+        const std::optional<aidl::android::hardware::graphics::common::Smpte2086>& smpte2086,
+        android::hardware::hidl_vec<uint8_t>* outSmpte2086);
+status_t decodeSmpte2086(
+        const android::hardware::hidl_vec<uint8_t>& smpte2086,
+        std::optional<aidl::android::hardware::graphics::common::Smpte2086>* outSmpte2086);
+
+status_t encodeCta861_3(
+        const std::optional<aidl::android::hardware::graphics::common::Cta861_3>& cta861_3,
+        android::hardware::hidl_vec<uint8_t>* outCta861_3);
+status_t decodeCta861_3(
+        const android::hardware::hidl_vec<uint8_t>& cta861_3,
+        std::optional<aidl::android::hardware::graphics::common::Cta861_3>* outCta861_3);
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+                            android::hardware::hidl_vec<uint8_t>* outSmpte2094_40);
+status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
+                            std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+/**
+ * The functions below can be used to encode and decode vendor metadata types.
+ */
+status_t encodeUint32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        uint32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, uint32_t* output);
+
+status_t encodeInt32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        int32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt32(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, int32_t* output);
+
+status_t encodeUint64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        uint64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, uint64_t* output);
+
+status_t encodeInt64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        int64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt64(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, int64_t* output);
+
+status_t encodeFloat(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        float input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeFloat(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, float* output);
+
+status_t encodeDouble(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        double input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeDouble(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, double* output);
+
+status_t encodeString(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const std::string& input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeString(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+        const android::hardware::hidl_vec<uint8_t>& input, std::string* output);
+
+/**
+ * The functions below can be used to parse extendable types.
+ */
+bool isStandardMetadataType(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+bool isStandardCompression(
+        const aidl::android::hardware::graphics::common::ExtendableType& compression);
+bool isStandardInterlaced(
+        const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+bool isStandardChromaSiting(
+        const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+bool isStandardPlaneLayoutComponentType(
+        const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(
+        const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+aidl::android::hardware::graphics::common::Compression getStandardCompressionValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& compression);
+aidl::android::hardware::graphics::common::Interlaced getStandardInterlacedValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+aidl::android::hardware::graphics::common::ChromaSiting getStandardChromaSitingValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+aidl::android::hardware::graphics::common::PlaneLayoutComponentType
+getStandardPlaneLayoutComponentTypeValue(
+        const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+/**
+ * The functions below return string representations of ExtendableTypes
+ */
+std::string getCompressionName(
+        const aidl::android::hardware::graphics::common::ExtendableType& compression);
+std::string getInterlacedName(
+        const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+std::string getChromaSitingName(
+        const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+std::string getPlaneLayoutComponentTypeName(
+        const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/vulkan/tools/Android.bp b/libs/gralloc/types/tests/Android.bp
similarity index 64%
rename from vulkan/tools/Android.bp
rename to libs/gralloc/types/tests/Android.bp
index 91d64fe..b939c1d 100644
--- a/vulkan/tools/Android.bp
+++ b/libs/gralloc/types/tests/Android.bp
@@ -1,4 +1,5 @@
-// Copyright 2015 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.
@@ -11,25 +12,14 @@
 // WITHOUT 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_binary {
-    name: "vkinfo",
-
-    clang: true,
-    cflags: [
-        "-fvisibility=hidden",
-        "-fstrict-aliasing",
-
-        "-DLOG_TAG=\"vkinfo\"",
-
-        "-Wextra",
-        "-Werror",
-    ],
-
-    srcs: ["vkinfo.cpp"],
-
+cc_test {
+    name: "GrallocTypes_test",
     shared_libs: [
-        "libvulkan",
-        "liblog",
+        "libgralloctypes",
+        "libhidlbase",
     ],
+    srcs: ["Gralloc4_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
 }
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
new file mode 100644
index 0000000..89cbf4a
--- /dev/null
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -0,0 +1,585 @@
+/*
+ * 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 "Gralloc4Test"
+
+#include <limits>
+
+#include <gralloctypes/Gralloc4.h>
+
+#include <gtest/gtest.h>
+
+using android::hardware::hidl_vec;
+
+using android::hardware::graphics::common::V1_2::PixelFormat;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
+
+using BufferDescriptorInfo = android::hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+template<class T>
+using EncodeFunction = status_t(*)(T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeConstFunction = status_t(*)(const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeFunction = status_t(*)(const MetadataType&, T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeConstFunction = status_t(*)(const MetadataType&, const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeOptionalFunction = status_t(*)(const std::optional<T>&, hidl_vec<uint8_t>*);
+
+template<class T>
+using DecodeFunction = status_t(*)(const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeMetadataTypeFunction = status_t(*)(const MetadataType&, const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeOptionalFunction = status_t(*)(const hidl_vec<uint8_t>&, std::optional<T>*);
+
+template<class T>
+void testHelper(const T& input, EncodeFunction<T> encode, DecodeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperConst(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataType(const T& input, EncodeMetadataTypeFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataTypeConst(const T& input, EncodeMetadataTypeConstFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+    ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperStableAidlType(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    T output;
+    ASSERT_EQ(NO_ERROR, encode(input, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_TRUE(input == output);
+}
+
+template<class T>
+void testHelperStableAidlTypeOptional(const std::optional<T>& input, EncodeOptionalFunction<T> encode,
+                                      DecodeOptionalFunction<T> decode) {
+    hidl_vec<uint8_t> vec;
+    std::optional<T> tmp = input;
+    std::optional<T> output;
+    ASSERT_EQ(NO_ERROR, encode(tmp, &vec));
+    ASSERT_EQ(NO_ERROR, decode(vec, &output));
+    ASSERT_EQ(tmp.has_value(), output.has_value());
+    if (!tmp.has_value()) {
+        return;
+    }
+    ASSERT_TRUE(*tmp == *output);
+}
+
+class Gralloc4TestUint32 : public testing::TestWithParam<uint32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestUint32Params, Gralloc4TestUint32,
+        ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint32_t>::min(),
+                          std::numeric_limits<uint32_t>::max()));
+
+TEST_P(Gralloc4TestUint32, Uint32) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint32, gralloc4::decodeUint32));
+}
+
+TEST_P(Gralloc4TestUint32, PixelFormatFourCC) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatFourCC, gralloc4::decodePixelFormatFourCC));
+}
+
+class Gralloc4TestInt32 : public testing::TestWithParam<int32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestInt32Params, Gralloc4TestInt32,
+        ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int32_t>::min(),
+                          std::numeric_limits<int32_t>::max()));
+
+TEST_P(Gralloc4TestInt32, Int32) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt32, gralloc4::decodeInt32));
+}
+
+class Gralloc4TestUint64 : public testing::TestWithParam<uint64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestUint64Params, Gralloc4TestUint64,
+        ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint64_t>::min(),
+                          std::numeric_limits<uint64_t>::max()));
+
+TEST_P(Gralloc4TestUint64, Uint64) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint64, gralloc4::decodeUint64));
+}
+
+TEST_P(Gralloc4TestUint64, BufferId) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeBufferId, gralloc4::decodeBufferId));
+}
+
+TEST_P(Gralloc4TestUint64, Width) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeWidth, gralloc4::decodeWidth));
+}
+
+TEST_P(Gralloc4TestUint64, Height) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeHeight, gralloc4::decodeHeight));
+}
+
+TEST_P(Gralloc4TestUint64, LayerCount) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeLayerCount, gralloc4::decodeLayerCount));
+}
+
+TEST_P(Gralloc4TestUint64, PixelFormatModifier) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatModifier, gralloc4::decodePixelFormatModifier));
+}
+
+TEST_P(Gralloc4TestUint64, Usage) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeUsage, gralloc4::decodeUsage));
+}
+
+TEST_P(Gralloc4TestUint64, AllocationSize) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeAllocationSize, gralloc4::decodeAllocationSize));
+}
+
+TEST_P(Gralloc4TestUint64, ProtectedContent) {
+    ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeProtectedContent, gralloc4::decodeProtectedContent));
+}
+
+class Gralloc4TestInt64 : public testing::TestWithParam<int64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestInt64Params, Gralloc4TestInt64,
+        ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int64_t>::min(),
+                          std::numeric_limits<int64_t>::max()));
+
+TEST_P(Gralloc4TestInt64, Int64) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt64, gralloc4::decodeInt64));
+}
+
+class Gralloc4TestFloat : public testing::TestWithParam<float> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestFloatParams, Gralloc4TestFloat,
+        ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<float>::min(),
+                          std::numeric_limits<float>::max()));
+
+TEST_P(Gralloc4TestFloat, Float) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeFloat, gralloc4::decodeFloat));
+}
+
+class Gralloc4TestDouble : public testing::TestWithParam<double> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestDoubleParams, Gralloc4TestDouble,
+        ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<double>::min(),
+                          std::numeric_limits<double>::max()));
+
+TEST_P(Gralloc4TestDouble, Double) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeDouble, gralloc4::decodeDouble));
+}
+
+class Gralloc4TestString : public testing::TestWithParam<std::string> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestStringParams, Gralloc4TestString,
+        ::testing::Values("name", "aaaaa", "", "abcdefghijklmnopqrstuvwxyz", "0xFF"));
+
+TEST_P(Gralloc4TestString, String) {
+    ASSERT_NO_FATAL_FAILURE(testHelperMetadataTypeConst(GetParam(), gralloc4::encodeString, gralloc4::decodeString));
+}
+
+TEST_P(Gralloc4TestString, Name) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeName, gralloc4::decodeName));
+}
+
+class Gralloc4TestPixelFormat : public testing::TestWithParam<PixelFormat> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestPixelFormatParams, Gralloc4TestPixelFormat,
+        ::testing::Values(PixelFormat::RGBA_8888, PixelFormat::BLOB,
+                          PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888,
+                          PixelFormat::YV12));
+
+TEST_P(Gralloc4TestPixelFormat, PixelFormatRequested) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodePixelFormatRequested, gralloc4::decodePixelFormatRequested));
+}
+
+class Gralloc4TestCompression : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestCompressionParams, Gralloc4TestCompression,
+        ::testing::Values(gralloc4::Compression_None, gralloc4::Compression_DisplayStreamCompression,
+            ExtendableType{"", 0},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Compression", 0xFF},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Compression", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestCompression, Compression) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeCompression, gralloc4::decodeCompression));
+}
+
+class Gralloc4TestInterlaced : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestInterlacedParams, Gralloc4TestInterlaced,
+        ::testing::Values(gralloc4::Interlaced_None, gralloc4::Interlaced_TopBottom,
+            gralloc4::Interlaced_RightLeft,
+            ExtendableType{"", 0},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", 0xFF},
+            ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestInterlaced, Interlaced) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeInterlaced, gralloc4::decodeInterlaced));
+}
+
+class Gralloc4TestChromaSiting : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestChromaSitingParams, Gralloc4TestChromaSiting,
+        ::testing::Values(gralloc4::ChromaSiting_None, gralloc4::ChromaSiting_Unknown,
+            gralloc4::ChromaSiting_SitedInterstitial, gralloc4::ChromaSiting_CositedHorizontal,
+            ExtendableType{"", 0},
+            ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", 0xFF},
+            ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestChromaSiting, ChromaSiting) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeChromaSiting, gralloc4::decodeChromaSiting));
+}
+
+class Gralloc4TestPlaneLayouts : public testing::Test { };
+
+TEST_F(Gralloc4TestPlaneLayouts, PlaneLayouts) {
+    uint32_t width = 64;
+    uint32_t height = 64;
+
+    std::vector<PlaneLayout> planeLayouts;
+    PlaneLayout planeLayoutA;
+    PlaneLayout planeLayoutRGB;
+    PlaneLayoutComponent component;
+
+    planeLayoutA.offsetInBytes = 0;
+    planeLayoutA.sampleIncrementInBits = 8;
+    planeLayoutA.strideInBytes = width + 20;
+    planeLayoutA.widthInSamples = width;
+    planeLayoutA.heightInSamples = height;
+    planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * height;
+    planeLayoutA.horizontalSubsampling = 1;
+    planeLayoutA.verticalSubsampling = 1;
+
+    component.type = gralloc4::PlaneLayoutComponentType_A;
+    component.offsetInBits = 0;
+    component.sizeInBits = 8;
+    planeLayoutA.components.push_back(component);
+
+    planeLayouts.push_back(planeLayoutA);
+
+    planeLayoutRGB.offsetInBytes = 0;
+    planeLayoutRGB.sampleIncrementInBits = 32;
+    planeLayoutRGB.strideInBytes = width + 20;
+    planeLayoutRGB.widthInSamples = width;
+    planeLayoutRGB.heightInSamples = height;
+    planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * height;
+    planeLayoutRGB.horizontalSubsampling = 1;
+    planeLayoutRGB.verticalSubsampling = 1;
+    component.type = gralloc4::PlaneLayoutComponentType_R;
+    planeLayoutRGB.components.push_back(component);
+    component.type = gralloc4::PlaneLayoutComponentType_G;
+    planeLayoutRGB.components.push_back(component);
+    component.type = gralloc4::PlaneLayoutComponentType_B;
+    planeLayoutRGB.components.push_back(component);
+
+    planeLayouts.push_back(planeLayoutRGB);
+
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(planeLayouts, gralloc4::encodePlaneLayouts, gralloc4::decodePlaneLayouts));
+}
+
+class Gralloc4TestCrop : public testing::Test { };
+
+TEST_F(Gralloc4TestCrop, Crop) {
+    std::vector<Rect> crops;
+    Rect crop1, crop2, crop3;
+
+    crop1.left = 0;
+    crop1.top = 0;
+    crop1.right = 64;
+    crop1.bottom = 64;
+    crops.push_back(crop1);
+
+    crop2.left = std::numeric_limits<int32_t>::min();
+    crop2.top = 0xFF;
+    crop2.right = std::numeric_limits<int32_t>::max();
+    crop2.bottom = 0xFFFF;
+    crops.push_back(crop2);
+
+    crop3.left = 0;
+    crop3.top = 0;
+    crop3.right = -1;
+    crop3.bottom = -1;
+    crops.push_back(crop3);
+
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(crops, gralloc4::encodeCrop, gralloc4::decodeCrop));
+}
+
+class Gralloc4TestDataspace : public testing::TestWithParam<Dataspace> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestDataspaceParams, Gralloc4TestDataspace,
+        ::testing::Values(Dataspace::UNKNOWN, Dataspace::ARBITRARY, Dataspace::DISPLAY_P3,
+                          Dataspace::ADOBE_RGB));
+
+TEST_P(Gralloc4TestDataspace, DataspaceRequested) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeDataspace, gralloc4::decodeDataspace));
+}
+
+class Gralloc4TestBlendMode : public testing::TestWithParam<BlendMode> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestBlendModeParams, Gralloc4TestBlendMode,
+        ::testing::Values(BlendMode::INVALID, BlendMode::NONE, BlendMode::PREMULTIPLIED,
+                          BlendMode::COVERAGE));
+
+TEST_P(Gralloc4TestBlendMode, BlendMode) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBlendMode, gralloc4::decodeBlendMode));
+}
+
+class Gralloc4TestSmpte2086 : public testing::TestWithParam<std::optional<Smpte2086>> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestSmpte2086Params, Gralloc4TestSmpte2086,
+        ::testing::Values(std::optional<Smpte2086>(Smpte2086{XyColor{0.680, 0.320},
+                                                             XyColor{0.265, 0.690},
+                                                             XyColor{0.150, 0.060},
+                                                             XyColor{0.3127, 0.3290},
+                                                             100.0, 0.1}),
+                          std::optional<Smpte2086>(Smpte2086{XyColor{-1.0, 100.0},
+                                                             XyColor{0xFF, -0xFF},
+                                                             XyColor{999.9, 0.0},
+                                                             XyColor{0.0, -1.0},
+                                                             -0.1, -100.0}),
+                          std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2086, Smpte2086) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2086, gralloc4::decodeSmpte2086));
+}
+
+class Gralloc4TestCta861_3 : public testing::TestWithParam<std::optional<Cta861_3>> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestCta861_3Params, Gralloc4TestCta861_3,
+        ::testing::Values(std::optional<Cta861_3>(Cta861_3{78.0, 62.0}),
+                          std::optional<Cta861_3>(Cta861_3{10.0, 10.0}),
+                          std::optional<Cta861_3>(Cta861_3{0.0, 0.0}),
+                          std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::min(), std::numeric_limits<float>::min()}),
+                          std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::max(), std::numeric_limits<float>::max()}),
+                          std::nullopt));
+
+TEST_P(Gralloc4TestCta861_3, Cta861_3) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeCta861_3, gralloc4::decodeCta861_3));
+}
+
+class Gralloc4TestSmpte2094_40 : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestSmpte2094_40Params, Gralloc4TestSmpte2094_40,
+        ::testing::Values(std::optional<std::vector<uint8_t>>({}),
+                          std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+                          std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+                                                               std::numeric_limits<uint8_t>::min() + 1,
+                                                               std::numeric_limits<uint8_t>::min() + 2,
+                                                               std::numeric_limits<uint8_t>::min() + 3,
+                                                               std::numeric_limits<uint8_t>::min() + 4}),
+                          std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+                                                               std::numeric_limits<uint8_t>::max() - 1,
+                                                               std::numeric_limits<uint8_t>::max() - 2,
+                                                               std::numeric_limits<uint8_t>::max() - 3,
+                                                               std::numeric_limits<uint8_t>::max() - 4}),
+                          std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_40, Smpte2094_40) {
+    ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
+}
+
+class Gralloc4TestBufferDescriptorInfo : public testing::TestWithParam<BufferDescriptorInfo> { };
+
+INSTANTIATE_TEST_CASE_P(
+        Gralloc4TestBufferDescriptorInfoParams, Gralloc4TestBufferDescriptorInfo,
+        ::testing::Values(BufferDescriptorInfo{"BufferName", 64, 64, 1,
+                PixelFormat::RGBA_8888,
+                static_cast<uint64_t>(BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN),
+                1024}));
+
+TEST_P(Gralloc4TestBufferDescriptorInfo, BufferDescriptorInfo) {
+    ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBufferDescriptorInfo, gralloc4::decodeBufferDescriptorInfo));
+}
+
+class Gralloc4TestErrors : public testing::Test { };
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestEncodeNull) {
+    ASSERT_NE(NO_ERROR, gralloc4::encodeBufferId(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeName("", nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeWidth(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeHeight(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeLayerCount(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatRequested(PixelFormat::RGBA_8888, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatFourCC(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatModifier(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeUsage(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeAllocationSize(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeProtectedContent(0, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeCompression(gralloc4::Compression_None, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeInterlaced(gralloc4::Interlaced_None, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeChromaSiting(gralloc4::ChromaSiting_None, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodePlaneLayouts({}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeDataspace(Dataspace::UNKNOWN, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeBlendMode(BlendMode::NONE, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
+    hidl_vec<uint8_t> vec;
+
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
+    hidl_vec<uint8_t> vec = { 0 };
+
+    uint64_t bufferId, width, height, layerCount, pixelFormatModifier, usage, allocationSize,
+            protectedContent;
+    std::string name;
+    PixelFormat pixelFormatRequested;
+    uint32_t pixelFormatFourCC;
+    ExtendableType compression, interlaced, chromaSiting;
+    std::vector<PlaneLayout> planeLayouts;
+    Dataspace dataspace;
+    BlendMode blendMode;
+    std::optional<Smpte2086> smpte2086;
+    std::optional<Cta861_3> cta861_3;
+    std::optional<std::vector<uint8_t>> smpte2094_40;
+
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, &bufferId));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, &name));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, &width));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, &height));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, &layerCount));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, &usage));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, &compression));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting));
+    ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, &smpte2086));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, &cta861_3));
+    ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, &smpte2094_40));
+}
+
+class Gralloc4TestHelpers : public testing::Test { };
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsStandard) {
+    ASSERT_TRUE(gralloc4::isStandardMetadataType(gralloc4::MetadataType_BufferId));
+    ASSERT_TRUE(gralloc4::isStandardCompression(gralloc4::Compression_None));
+    ASSERT_TRUE(gralloc4::isStandardInterlaced(gralloc4::Interlaced_None));
+    ASSERT_TRUE(gralloc4::isStandardChromaSiting(gralloc4::ChromaSiting_None));
+    ASSERT_TRUE(gralloc4::isStandardPlaneLayoutComponentType(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsNotStandard) {
+    ASSERT_FALSE(gralloc4::isStandardMetadataType({"vendor.mycompanyname.graphics.common.MetadataType", 0}));
+    ASSERT_FALSE(gralloc4::isStandardCompression({"vendor.mycompanyname.graphics.common.Compression", 0}));
+    ASSERT_FALSE(gralloc4::isStandardInterlaced({"vendor.mycompanyname.graphics.common.Interlaced", 0}));
+    ASSERT_FALSE(gralloc4::isStandardChromaSiting({"vendor.mycompanyname.graphics.common.ChromaSiting", 0}));
+    ASSERT_FALSE(gralloc4::isStandardPlaneLayoutComponentType({"vendor.mycompanyname.graphics.common.PlaneLayoutComponentType", 0}));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestGetStandardValue) {
+    ASSERT_EQ(StandardMetadataType::BUFFER_ID, gralloc4::getStandardMetadataTypeValue(gralloc4::MetadataType_BufferId));
+    ASSERT_EQ(Compression::NONE, gralloc4::getStandardCompressionValue(gralloc4::Compression_None));
+    ASSERT_EQ(Interlaced::NONE, gralloc4::getStandardInterlacedValue(gralloc4::Interlaced_None));
+    ASSERT_EQ(ChromaSiting::NONE, gralloc4::getStandardChromaSitingValue(gralloc4::ChromaSiting_None));
+    ASSERT_EQ(PlaneLayoutComponentType::Y, gralloc4::getStandardPlaneLayoutComponentTypeValue(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+} // namespace android
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 56521bf..642c5f2 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -32,5 +32,9 @@
         "libutils",
     ],
 
+    header_libs: [
+        "libnativeloader-headers",
+    ],
+
     export_include_dirs: ["include"],
 }
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index 4a801be..f2d0943 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -86,6 +86,8 @@
     if ((status = parcel->writeInt64Vector(vkDriverLoadingTime)) != OK) return status;
     if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status;
     if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status;
+    if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
+    if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
     return OK;
 }
 
@@ -97,6 +99,8 @@
     if ((status = parcel->readInt64Vector(&vkDriverLoadingTime)) != OK) return status;
     if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status;
     if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status;
+    if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
+    if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
     return OK;
 }
 
@@ -105,6 +109,8 @@
     StringAppendF(&result, "appPackageName = %s\n", appPackageName.c_str());
     StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode);
     StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse);
+    StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
+    StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
     result.append("glDriverLoadingTime:");
     for (int32_t loadingTime : glDriverLoadingTime) {
         StringAppendF(&result, " %d", loadingTime);
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 6e59cc5..befabee 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -32,6 +32,7 @@
 #include <cutils/properties.h>
 #include <graphicsenv/IGpuService.h>
 #include <log/log.h>
+#include <nativeloader/dlext_namespaces.h>
 #include <sys/prctl.h>
 #include <utils/Trace.h>
 
@@ -39,22 +40,6 @@
 #include <string>
 #include <thread>
 
-// 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);
-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,
-};
-}
-
 // TODO(ianelliott@): Get the following from an ANGLE header:
 #define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting
 // Version-2 API:
@@ -160,17 +145,23 @@
 void GraphicsEnv::hintActivityLaunch() {
     ATRACE_CALL();
 
+    {
+        std::lock_guard<std::mutex> lock(mStatsLock);
+        if (mActivityLaunched) return;
+        mActivityLaunched = true;
+    }
+
     std::thread trySendGpuStatsThread([this]() {
         // If there's already graphics driver preloaded in the process, just send
         // the stats info to GpuStats directly through async binder.
         std::lock_guard<std::mutex> lock(mStatsLock);
         if (mGpuStats.glDriverToSend) {
             mGpuStats.glDriverToSend = false;
-            sendGpuStatsLocked(GraphicsEnv::Api::API_GL, true, mGpuStats.glDriverLoadingTime);
+            sendGpuStatsLocked(GpuStatsInfo::Api::API_GL, true, mGpuStats.glDriverLoadingTime);
         }
         if (mGpuStats.vkDriverToSend) {
             mGpuStats.vkDriverToSend = false;
-            sendGpuStatsLocked(GraphicsEnv::Api::API_VK, true, mGpuStats.vkDriverLoadingTime);
+            sendGpuStatsLocked(GpuStatsInfo::Api::API_VK, true, mGpuStats.vkDriverLoadingTime);
         }
     });
     trySendGpuStatsThread.detach();
@@ -201,34 +192,34 @@
     mGpuStats.vulkanVersion = vulkanVersion;
 }
 
-void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) {
+void GraphicsEnv::setDriverToLoad(GpuStatsInfo::Driver driver) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mStatsLock);
     switch (driver) {
-        case GraphicsEnv::Driver::GL:
-        case GraphicsEnv::Driver::GL_UPDATED:
-        case GraphicsEnv::Driver::ANGLE: {
-            if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE ||
-                mGpuStats.glDriverToLoad == GraphicsEnv::Driver::GL) {
+        case GpuStatsInfo::Driver::GL:
+        case GpuStatsInfo::Driver::GL_UPDATED:
+        case GpuStatsInfo::Driver::ANGLE: {
+            if (mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::NONE ||
+                mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::GL) {
                 mGpuStats.glDriverToLoad = driver;
                 break;
             }
 
-            if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) {
+            if (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE) {
                 mGpuStats.glDriverFallback = driver;
             }
             break;
         }
-        case Driver::VULKAN:
-        case Driver::VULKAN_UPDATED: {
-            if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE ||
-                mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::VULKAN) {
+        case GpuStatsInfo::Driver::VULKAN:
+        case GpuStatsInfo::Driver::VULKAN_UPDATED: {
+            if (mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::NONE ||
+                mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::VULKAN) {
                 mGpuStats.vkDriverToLoad = driver;
                 break;
             }
 
-            if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) {
+            if (mGpuStats.vkDriverFallback == GpuStatsInfo::Driver::NONE) {
                 mGpuStats.vkDriverFallback = driver;
             }
             break;
@@ -238,17 +229,16 @@
     }
 }
 
-void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded,
+void GraphicsEnv::setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded,
                                   int64_t driverLoadingTime) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mStatsLock);
-    const bool doNotSend = mGpuStats.appPackageName.empty();
-    if (api == GraphicsEnv::Api::API_GL) {
-        if (doNotSend) mGpuStats.glDriverToSend = true;
+    if (api == GpuStatsInfo::Api::API_GL) {
+        mGpuStats.glDriverToSend = true;
         mGpuStats.glDriverLoadingTime = driverLoadingTime;
     } else {
-        if (doNotSend) mGpuStats.vkDriverToSend = true;
+        mGpuStats.vkDriverToSend = true;
         mGpuStats.vkDriverLoadingTime = driverLoadingTime;
     }
 
@@ -256,7 +246,7 @@
 }
 
 static sp<IGpuService> getGpuService() {
-    const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
+    static const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu"));
     if (!binder) {
         ALOGE("Failed to get gpu service");
         return nullptr;
@@ -265,10 +255,18 @@
     return interface_cast<IGpuService>(binder);
 }
 
-void GraphicsEnv::setTargetStats(const Stats stats, const uint64_t value) {
+bool GraphicsEnv::readyToSendGpuStatsLocked() {
+    // Only send stats for processes having at least one activity launched and that process doesn't
+    // skip the GraphicsEnvironment setup.
+    return mActivityLaunched && !mGpuStats.appPackageName.empty();
+}
+
+void GraphicsEnv::setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mStatsLock);
+    if (!readyToSendGpuStatsLocked()) return;
+
     const sp<IGpuService> gpuService = getGpuService();
     if (gpuService) {
         gpuService->setTargetStats(mGpuStats.appPackageName, mGpuStats.driverVersionCode, stats,
@@ -276,12 +274,11 @@
     }
 }
 
-void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded,
+void GraphicsEnv::sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded,
                                      int64_t driverLoadingTime) {
     ATRACE_CALL();
 
-    // Do not sendGpuStats for those skipping the GraphicsEnvironment setup
-    if (mGpuStats.appPackageName.empty()) return;
+    if (!readyToSendGpuStatsLocked()) return;
 
     ALOGV("sendGpuStats:\n"
           "\tdriverPackageName[%s]\n"
@@ -297,16 +294,16 @@
           mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(),
           mGpuStats.vulkanVersion, static_cast<int32_t>(api), isDriverLoaded, driverLoadingTime);
 
-    GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE;
+    GpuStatsInfo::Driver driver = GpuStatsInfo::Driver::NONE;
     bool isIntendedDriverLoaded = false;
-    if (api == GraphicsEnv::Api::API_GL) {
+    if (api == GpuStatsInfo::Api::API_GL) {
         driver = mGpuStats.glDriverToLoad;
         isIntendedDriverLoaded =
-                isDriverLoaded && (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE);
+                isDriverLoaded && (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE);
     } else {
         driver = mGpuStats.vkDriverToLoad;
         isIntendedDriverLoaded =
-                isDriverLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE);
+                isDriverLoaded && (mGpuStats.vkDriverFallback == GpuStatsInfo::Driver::NONE);
     }
 
     const sp<IGpuService> gpuService = getGpuService();
@@ -318,6 +315,13 @@
     }
 }
 
+bool GraphicsEnv::setInjectLayersPrSetDumpable() {
+    if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+        return false;
+    }
+    return true;
+}
+
 void* GraphicsEnv::loadLibrary(std::string name) {
     const android_dlextinfo dlextinfo = {
             .flags = ANDROID_DLEXT_USE_NAMESPACE,
@@ -607,6 +611,10 @@
     return mDriverNamespace;
 }
 
+std::string GraphicsEnv::getDriverPath() const {
+    return mDriverPath;
+}
+
 android_namespace_t* GraphicsEnv::getAngleNamespace() {
     std::lock_guard<std::mutex> lock(mNamespaceMutex);
 
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index db16f3c..de3503b 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -30,7 +30,7 @@
     virtual void setGpuStats(const std::string& driverPackageName,
                              const std::string& driverVersionName, uint64_t driverVersionCode,
                              int64_t driverBuildTime, const std::string& appPackageName,
-                             const int32_t vulkanVersion, GraphicsEnv::Driver driver,
+                             const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
                              bool isDriverLoaded, int64_t driverLoadingTime) {
         Parcel data, reply;
         data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -48,52 +48,8 @@
         remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY);
     }
 
-    virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
-        if (!outStats) return UNEXPECTED_NULL;
-
-        Parcel data, reply;
-        status_t status;
-
-        if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK)
-            return status;
-
-        if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_GLOBAL_INFO, data, &reply)) !=
-            OK)
-            return status;
-
-        int32_t result = 0;
-        if ((status = reply.readInt32(&result)) != OK) return status;
-        if (result != OK) return result;
-
-        outStats->clear();
-        return reply.readParcelableVector(outStats);
-    }
-
-    virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
-        if (!outStats) return UNEXPECTED_NULL;
-
-        Parcel data, reply;
-        status_t status;
-
-        if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) {
-            return status;
-        }
-
-        if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_APP_INFO, data, &reply)) !=
-            OK) {
-            return status;
-        }
-
-        int32_t result = 0;
-        if ((status = reply.readInt32(&result)) != OK) return status;
-        if (result != OK) return result;
-
-        outStats->clear();
-        return reply.readParcelableVector(outStats);
-    }
-
     virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                                const GraphicsEnv::Stats stats, const uint64_t value) {
+                                const GpuStatsInfo::Stats stats, const uint64_t value) {
         Parcel data, reply;
         data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
 
@@ -145,37 +101,11 @@
             if ((status = data.readInt64(&driverLoadingTime)) != OK) return status;
 
             setGpuStats(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
-                        appPackageName, vulkanVersion, static_cast<GraphicsEnv::Driver>(driver),
+                        appPackageName, vulkanVersion, static_cast<GpuStatsInfo::Driver>(driver),
                         isDriverLoaded, driverLoadingTime);
 
             return OK;
         }
-        case GET_GPU_STATS_GLOBAL_INFO: {
-            CHECK_INTERFACE(IGpuService, data, reply);
-
-            std::vector<GpuStatsGlobalInfo> stats;
-            const status_t result = getGpuStatsGlobalInfo(&stats);
-
-            if ((status = reply->writeInt32(result)) != OK) return status;
-            if (result != OK) return result;
-
-            if ((status = reply->writeParcelableVector(stats)) != OK) return status;
-
-            return OK;
-        }
-        case GET_GPU_STATS_APP_INFO: {
-            CHECK_INTERFACE(IGpuService, data, reply);
-
-            std::vector<GpuStatsAppInfo> stats;
-            const status_t result = getGpuStatsAppInfo(&stats);
-
-            if ((status = reply->writeInt32(result)) != OK) return status;
-            if (result != OK) return result;
-
-            if ((status = reply->writeParcelableVector(stats)) != OK) return status;
-
-            return OK;
-        }
         case SET_TARGET_STATS: {
             CHECK_INTERFACE(IGpuService, data, reply);
 
@@ -192,7 +122,7 @@
             if ((status = data.readUint64(&value)) != OK) return status;
 
             setTargetStats(appPackageName, driverVersionCode,
-                           static_cast<GraphicsEnv::Stats>(stats), value);
+                           static_cast<GpuStatsInfo::Stats>(stats), value);
 
             return OK;
         }
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index edcccfe..9aba69f 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -70,6 +70,53 @@
     std::vector<int64_t> vkDriverLoadingTime = {};
     std::vector<int64_t> angleDriverLoadingTime = {};
     bool cpuVulkanInUse = false;
+    bool falsePrerotation = false;
+    bool gles1InUse = false;
+};
+
+/*
+ * class for holding the gpu stats in GraphicsEnv before sending to GpuService.
+ */
+class GpuStatsInfo {
+public:
+    enum Api {
+        API_GL = 0,
+        API_VK = 1,
+    };
+
+    enum Driver {
+        NONE = 0,
+        GL = 1,
+        GL_UPDATED = 2,
+        VULKAN = 3,
+        VULKAN_UPDATED = 4,
+        ANGLE = 5,
+    };
+
+    enum Stats {
+        CPU_VULKAN_IN_USE = 0,
+        FALSE_PREROTATION = 1,
+        GLES_1_IN_USE = 2,
+    };
+
+    GpuStatsInfo() = default;
+    GpuStatsInfo(const GpuStatsInfo&) = default;
+    virtual ~GpuStatsInfo() = default;
+
+    std::string driverPackageName = "";
+    std::string driverVersionName = "";
+    uint64_t driverVersionCode = 0;
+    int64_t driverBuildTime = 0;
+    std::string appPackageName = "";
+    int32_t vulkanVersion = 0;
+    Driver glDriverToLoad = Driver::NONE;
+    Driver glDriverFallback = Driver::NONE;
+    Driver vkDriverToLoad = Driver::NONE;
+    Driver vkDriverFallback = Driver::NONE;
+    bool glDriverToSend = false;
+    bool vkDriverToSend = false;
+    int64_t glDriverLoadingTime = 0;
+    int64_t vkDriverLoadingTime = 0;
 };
 
 } // namespace android
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 227b458..22a2332 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_UI_GRAPHICS_ENV_H
 #define ANDROID_UI_GRAPHICS_ENV_H 1
 
+#include <graphicsenv/GpuStatsInfo.h>
+
 #include <mutex>
 #include <string>
 #include <vector>
@@ -29,59 +31,6 @@
 
 class GraphicsEnv {
 public:
-    enum Api {
-        API_GL = 0,
-        API_VK = 1,
-    };
-
-    enum Driver {
-        NONE = 0,
-        GL = 1,
-        GL_UPDATED = 2,
-        VULKAN = 3,
-        VULKAN_UPDATED = 4,
-        ANGLE = 5,
-    };
-
-    enum Stats {
-        CPU_VULKAN_IN_USE = 0,
-    };
-
-private:
-    struct GpuStats {
-        std::string driverPackageName;
-        std::string driverVersionName;
-        uint64_t driverVersionCode;
-        int64_t driverBuildTime;
-        std::string appPackageName;
-        int32_t vulkanVersion;
-        Driver glDriverToLoad;
-        Driver glDriverFallback;
-        Driver vkDriverToLoad;
-        Driver vkDriverFallback;
-        bool glDriverToSend;
-        bool vkDriverToSend;
-        int64_t glDriverLoadingTime;
-        int64_t vkDriverLoadingTime;
-
-        GpuStats()
-              : driverPackageName(""),
-                driverVersionName(""),
-                driverVersionCode(0),
-                driverBuildTime(0),
-                appPackageName(""),
-                vulkanVersion(0),
-                glDriverToLoad(Driver::NONE),
-                glDriverFallback(Driver::NONE),
-                vkDriverToLoad(Driver::NONE),
-                vkDriverFallback(Driver::NONE),
-                glDriverToSend(false),
-                vkDriverToSend(false),
-                glDriverLoadingTime(0),
-                vkDriverLoadingTime(0) {}
-    };
-
-public:
     static GraphicsEnv& getInstance();
 
     // Check if the process is debuggable. It returns false except in any of the
@@ -95,6 +44,9 @@
     //    in the application manifest.
     bool isDebuggable();
 
+    /*
+     * Apis for updatable driver
+     */
     // 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
@@ -104,17 +56,40 @@
     // graphics drivers. The string is a list of libraries separated by ':',
     // which is required by android_link_namespaces.
     void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries);
+    // Get the updatable driver namespace.
     android_namespace_t* getDriverNamespace();
+    std::string getDriverPath() const;
+
+    /*
+     * Apis for GpuStats
+     */
+    // Hint there's real activity launching on the app process.
     void hintActivityLaunch();
+    // Set the initial GpuStats.
     void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
                      uint64_t versionCode, int64_t driverBuildTime,
                      const std::string& appPackageName, const int32_t vulkanVersion);
-    void setTargetStats(const Stats stats, const uint64_t value = 0);
-    void setDriverToLoad(Driver driver);
-    void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime);
-    void sendGpuStatsLocked(Api api, bool isDriverLoaded, int64_t driverLoadingTime);
+    // Set stats for target GpuStatsInfo::Stats type.
+    void setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value = 0);
+    // Set which driver is intended to load.
+    void setDriverToLoad(GpuStatsInfo::Driver driver);
+    // Set which driver is actually loaded.
+    void setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
 
+    /*
+     * Api for Vk/GL layer injection.  Presently, drivers enable certain
+     * profiling features when prctl(PR_GET_DUMPABLE) returns true.
+     * Calling this when layer injection metadata is present allows the driver
+     * to enable profiling even when in a non-debuggable app
+     */
+    bool setInjectLayersPrSetDumpable();
+
+    /*
+     * Apis for ANGLE
+     */
+    // Check if the requested app should use ANGLE.
     bool shouldUseAngle(std::string appName);
+    // Check if this app process should use ANGLE.
     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
@@ -123,43 +98,79 @@
     //     /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);
+    // Get the ANGLE driver namespace.
     android_namespace_t* getAngleNamespace();
+    // Get the app name for ANGLE debug message.
     std::string& getAngleAppName();
 
+    /*
+     * Apis for debug layer
+     */
+    // Set additional layer search paths.
     void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths);
+    // Get the app namespace for loading layers.
     NativeLoaderNamespace* getAppNamespace();
-
+    // Get additional layer search paths.
     const std::string& getLayerPaths();
-
+    // Set the Vulkan debug layers.
     void setDebugLayers(const std::string layers);
+    // Set the GL debug layers.
     void setDebugLayersGLES(const std::string layers);
+    // Get the debug layers to load.
     const std::string& getDebugLayers();
+    // Get the debug layers to load.
     const std::string& getDebugLayersGLES();
 
 private:
     enum UseAngle { UNKNOWN, YES, NO };
 
+    // Load requested ANGLE library.
     void* loadLibrary(std::string name);
+    // Check ANGLE support with the rules.
     bool checkAngleRules(void* so);
+    // Update whether ANGLE should be used.
     void updateUseAngle();
+    // Link updatable driver namespace with llndk and vndk-sp libs.
     bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace);
+    // Check whether this process is ready to send stats.
+    bool readyToSendGpuStatsLocked();
+    // Send the initial complete GpuStats to GpuService.
+    void sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime);
 
     GraphicsEnv() = default;
+    // Path to updatable driver libs.
     std::string mDriverPath;
+    // Path to additional sphal libs linked to updatable driver namespace.
     std::string mSphalLibraries;
+    // This mutex protects mGpuStats and get gpuservice call.
     std::mutex mStatsLock;
-    GpuStats mGpuStats;
+    // Cache the activity launch info
+    bool mActivityLaunched = false;
+    // Information bookkept for GpuStats.
+    GpuStatsInfo mGpuStats;
+    // Path to ANGLE libs.
     std::string mAnglePath;
+    // This App's name.
     std::string mAngleAppName;
+    // ANGLE developer opt in status.
     std::string mAngleDeveloperOptIn;
+    // ANGLE rules.
     std::vector<char> mRulesBuffer;
+    // Use ANGLE flag.
     UseAngle mUseAngle = UNKNOWN;
+    // Vulkan debug layers libs.
     std::string mDebugLayers;
+    // GL debug layers libs.
     std::string mDebugLayersGLES;
+    // Additional debug layers search path.
     std::string mLayerPaths;
+    // This mutex protects the namespace creation.
     std::mutex mNamespaceMutex;
+    // Updatable driver namespace.
     android_namespace_t* mDriverNamespace = nullptr;
+    // ANGLE namespace.
     android_namespace_t* mAngleNamespace = nullptr;
+    // This App's namespace.
     NativeLoaderNamespace* mAppNamespace = nullptr;
 };
 
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index b8d0bd1..c7c6d1e 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -16,12 +16,11 @@
 
 #pragma once
 
-#include <vector>
-
 #include <binder/IInterface.h>
 #include <cutils/compiler.h>
 #include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/GraphicsEnv.h>
+
+#include <vector>
 
 namespace android {
 
@@ -37,26 +36,18 @@
     virtual void setGpuStats(const std::string& driverPackageName,
                              const std::string& driverVersionName, uint64_t driverVersionCode,
                              int64_t driverBuildTime, const std::string& appPackageName,
-                             const int32_t vulkanVersion, GraphicsEnv::Driver driver,
+                             const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
                              bool isDriverLoaded, int64_t driverLoadingTime) = 0;
 
     // set target stats.
     virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                                const GraphicsEnv::Stats stats, const uint64_t value = 0) = 0;
-
-    // get GPU global stats from GpuStats module.
-    virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0;
-
-    // get GPU app stats from GpuStats module.
-    virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const = 0;
+                                const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0;
 };
 
 class BnGpuService : public BnInterface<IGpuService> {
 public:
     enum IGpuServiceTag {
         SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION,
-        GET_GPU_STATS_GLOBAL_INFO,
-        GET_GPU_STATS_APP_INFO,
         SET_TARGET_STATS,
         // Always append new enum to the end.
     };
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index f3d5aab..7f57f5d 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -40,17 +40,26 @@
     defaults: ["libgui_bufferqueue-defaults"],
 
     srcs: [
+        ":framework_native_aidl",
+        ":libgui_bufferqueue_sources",
+
         "BitTube.cpp",
+        "BLASTBufferQueue.cpp",
         "BufferHubConsumer.cpp",
         "BufferHubProducer.cpp",
         "BufferItemConsumer.cpp",
         "ConsumerBase.cpp",
         "CpuConsumer.cpp",
         "DebugEGLImageTracker.cpp",
+        "DisplayEventDispatcher.cpp",
         "DisplayEventReceiver.cpp",
         "GLConsumer.cpp",
         "GuiConfig.cpp",
+        "IConsumerListener.cpp",
         "IDisplayEventConnection.cpp",
+        "IGraphicBufferConsumer.cpp",
+        "IGraphicBufferProducer.cpp",
+        "IProducerListener.cpp",
         "IRegionSamplingListener.cpp",
         "ISurfaceComposer.cpp",
         "ISurfaceComposerClient.cpp",
@@ -58,22 +67,32 @@
         "LayerDebugInfo.cpp",
         "LayerMetadata.cpp",
         "LayerState.cpp",
+        "OccupancyTracker.cpp",
         "StreamSplitter.cpp",
         "Surface.cpp",
         "SurfaceControl.cpp",
         "SurfaceComposerClient.cpp",
         "SyncFeatures.cpp",
         "view/Surface.cpp",
+        "bufferqueue/1.0/B2HProducerListener.cpp",
+        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
+        "bufferqueue/2.0/B2HProducerListener.cpp",
+        "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
     ],
 
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
+        "libbinder",
         "libbufferhub",
         "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui.
         "libinput",
         "libpdx_default_transport",
     ],
 
+    export_shared_lib_headers: [
+        "libbinder",
+    ],
+
     // bufferhub is not used when building libgui for vendors
     target: {
         vendor: {
@@ -99,6 +118,10 @@
         "libdvr_headers",
         "libpdx_headers",
     ],
+
+    aidl: {
+        export_aidl_headers: true,
+    }
 }
 
 // Used by media codec services exclusively as a static lib for
@@ -109,9 +132,37 @@
 
     cflags: [
         "-DNO_BUFFERHUB",
+        "-DNO_BINDER",
     ],
 
     defaults: ["libgui_bufferqueue-defaults"],
+
+    srcs: [
+        ":libgui_bufferqueue_sources",
+    ],
+}
+
+filegroup {
+    name: "libgui_bufferqueue_sources",
+    srcs: [
+        "BufferItem.cpp",
+        "BufferQueue.cpp",
+        "BufferQueueConsumer.cpp",
+        "BufferQueueCore.cpp",
+        "BufferQueueProducer.cpp",
+        "BufferQueueThreadState.cpp",
+        "BufferSlot.cpp",
+        "FrameTimestamps.cpp",
+        "GLConsumerUtils.cpp",
+        "HdrMetadata.cpp",
+        "QueueBufferInputOutput.cpp",
+        "bufferqueue/1.0/Conversion.cpp",
+        "bufferqueue/1.0/H2BProducerListener.cpp",
+        "bufferqueue/1.0/WProducerListener.cpp",
+        "bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
+        "bufferqueue/2.0/H2BProducerListener.cpp",
+        "bufferqueue/2.0/types.cpp",
+    ],
 }
 
 // Common build config shared by libgui and libgui_bufferqueue_static.
@@ -138,34 +189,6 @@
         },
     },
 
-    srcs: [
-        "BufferItem.cpp",
-        "BufferQueue.cpp",
-        "BufferQueueConsumer.cpp",
-        "BufferQueueCore.cpp",
-        "BufferQueueProducer.cpp",
-        "BufferQueueThreadState.cpp",
-        "BufferSlot.cpp",
-        "FrameTimestamps.cpp",
-        "GLConsumerUtils.cpp",
-        "HdrMetadata.cpp",
-        "IConsumerListener.cpp",
-        "IGraphicBufferConsumer.cpp",
-        "IGraphicBufferProducer.cpp",
-        "IProducerListener.cpp",
-        "OccupancyTracker.cpp",
-        "bufferqueue/1.0/B2HProducerListener.cpp",
-        "bufferqueue/1.0/Conversion.cpp",
-        "bufferqueue/1.0/H2BGraphicBufferProducer.cpp",
-        "bufferqueue/1.0/H2BProducerListener.cpp",
-        "bufferqueue/1.0/WProducerListener.cpp",
-        "bufferqueue/2.0/B2HGraphicBufferProducer.cpp",
-        "bufferqueue/2.0/B2HProducerListener.cpp",
-        "bufferqueue/2.0/H2BGraphicBufferProducer.cpp",
-        "bufferqueue/2.0/H2BProducerListener.cpp",
-        "bufferqueue/2.0/types.cpp",
-    ],
-
     whole_static_libs: [
         "LibGuiProperties",
     ],
@@ -177,7 +200,6 @@
         "android.hardware.graphics.common@1.2",
         "android.hidl.token@1.0-utils",
         "libbase",
-        "libbinder",
         "libcutils",
         "libEGL",
         "libGLESv2",
@@ -200,7 +222,6 @@
     ],
 
     export_shared_lib_headers: [
-        "libbinder",
         "libEGL",
         "libnativewindow",
         "libui",
@@ -220,4 +241,21 @@
     ],
 }
 
+// GMocks for use by external code
+cc_library_static {
+    name: "libgui_mocks",
+    vendor_available: false,
+
+    defaults: ["libgui_bufferqueue-defaults"],
+    static_libs: [
+        "libgtest",
+        "libgmock",
+    ],
+
+    srcs: [
+        "mock/GraphicBufferConsumer.cpp",
+        "mock/GraphicBufferProducer.cpp",
+    ],
+}
+
 subdirs = ["tests"]
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
new file mode 100644
index 0000000..02416e4
--- /dev/null
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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 "BLASTBufferQueue"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <cutils/properties.h>
+
+#include <gui/BLASTBufferQueue.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/GLConsumer.h>
+
+#include <utils/Trace.h>
+
+#include <chrono>
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+void BLASTBufferItemConsumer::onDisconnect() {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mPreviouslyConnected = mCurrentlyConnected;
+    mCurrentlyConnected = false;
+    if (mPreviouslyConnected) {
+        mDisconnectEvents.push(mCurrentFrameNumber);
+    }
+    mFrameEventHistory.onDisconnect();
+}
+
+void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+                                                       FrameEventHistoryDelta* outDelta) {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    if (newTimestamps) {
+        // BufferQueueProducer only adds a new timestamp on
+        // queueBuffer
+        mCurrentFrameNumber = newTimestamps->frameNumber;
+        mFrameEventHistory.addQueue(*newTimestamps);
+    }
+    if (outDelta) {
+        // frame event histories will be processed
+        // only after the producer connects and requests
+        // deltas for the first time.  Forward this intent
+        // to SF-side to turn event processing back on
+        mPreviouslyConnected = mCurrentlyConnected;
+        mCurrentlyConnected = true;
+        mFrameEventHistory.getAndResetDelta(outDelta);
+    }
+}
+
+void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
+                                                    const sp<Fence>& glDoneFence,
+                                                    const sp<Fence>& presentFence,
+                                                    const sp<Fence>& prevReleaseFence,
+                                                    CompositorTiming compositorTiming,
+                                                    nsecs_t latchTime, nsecs_t dequeueReadyTime) {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+
+    // if the producer is not connected, don't bother updating,
+    // the next producer that connects won't access this frame event
+    if (!mCurrentlyConnected) return;
+    std::shared_ptr<FenceTime> glDoneFenceTime = std::make_shared<FenceTime>(glDoneFence);
+    std::shared_ptr<FenceTime> presentFenceTime = std::make_shared<FenceTime>(presentFence);
+    std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(prevReleaseFence);
+
+    mFrameEventHistory.addLatch(frameNumber, latchTime);
+    mFrameEventHistory.addRelease(frameNumber, dequeueReadyTime, std::move(releaseFenceTime));
+    mFrameEventHistory.addPreComposition(frameNumber, refreshStartTime);
+    mFrameEventHistory.addPostComposition(frameNumber, glDoneFenceTime, presentFenceTime,
+                                          compositorTiming);
+}
+
+void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) {
+    bool disconnect = false;
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    while (!mDisconnectEvents.empty() && mDisconnectEvents.front() <= frameNumber) {
+        disconnect = true;
+        mDisconnectEvents.pop();
+    }
+    if (needsDisconnect != nullptr) *needsDisconnect = disconnect;
+}
+
+BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
+      : mSurfaceControl(surface),
+        mWidth(width),
+        mHeight(height),
+        mNextTransaction(nullptr) {
+    BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+
+    int8_t disableTripleBuffer = property_get_bool("ro.sf.disable_triple_buffer", 0);
+    if (!disableTripleBuffer) {
+        mProducer->setMaxDequeuedBufferCount(2);
+    }
+    mBufferItemConsumer =
+            new BLASTBufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
+    static int32_t id = 0;
+    auto name = std::string("BLAST Consumer") + std::to_string(id);
+    id++;
+    mBufferItemConsumer->setName(String8(name.c_str()));
+    mBufferItemConsumer->setFrameAvailableListener(this);
+    mBufferItemConsumer->setBufferFreedListener(this);
+    mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+    mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
+    mTransformHint = mSurfaceControl->getTransformHint();
+
+    mNumAcquired = 0;
+    mNumFrameAvailable = 0;
+    mPendingReleaseItem.item = BufferItem();
+    mPendingReleaseItem.releaseFence = nullptr;
+}
+
+void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
+    std::unique_lock _lock{mMutex};
+    mSurfaceControl = surface;
+    mWidth = width;
+    mHeight = height;
+    mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
+}
+
+static void transactionCallbackThunk(void* context, nsecs_t latchTime,
+                                     const sp<Fence>& presentFence,
+                                     const std::vector<SurfaceControlStats>& stats) {
+    if (context == nullptr) {
+        return;
+    }
+    BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context);
+    bq->transactionCallback(latchTime, presentFence, stats);
+}
+
+void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
+                                           const std::vector<SurfaceControlStats>& stats) {
+    std::unique_lock _lock{mMutex};
+    ATRACE_CALL();
+
+    if (!stats.empty()) {
+        mTransformHint = stats[0].transformHint;
+        mBufferItemConsumer->setTransformHint(mTransformHint);
+        mBufferItemConsumer->updateFrameTimestamps(stats[0].frameEventStats.frameNumber,
+                                                   stats[0].frameEventStats.refreshStartTime,
+                                                   stats[0].frameEventStats.gpuCompositionDoneFence,
+                                                   stats[0].presentFence,
+                                                   stats[0].previousReleaseFence,
+                                                   stats[0].frameEventStats.compositorTiming,
+                                                   stats[0].latchTime,
+                                                   stats[0].frameEventStats.dequeueReadyTime);
+    }
+    if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+        if (!stats.empty()) {
+            mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+        } else {
+            ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+            mPendingReleaseItem.releaseFence = nullptr;
+        }
+        mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
+                                           mPendingReleaseItem.releaseFence
+                                                   ? mPendingReleaseItem.releaseFence
+                                                   : Fence::NO_FENCE);
+        mNumAcquired--;
+        mPendingReleaseItem.item = BufferItem();
+        mPendingReleaseItem.releaseFence = nullptr;
+    }
+
+    if (mSubmitted.empty()) {
+        ALOGE("ERROR: callback with no corresponding submitted buffer item");
+    }
+    mPendingReleaseItem.item = std::move(mSubmitted.front());
+    mSubmitted.pop();
+    if (mNextTransaction == nullptr) {
+        processNextBufferLocked();
+    }
+    mCallbackCV.notify_all();
+    decStrong((void*)transactionCallbackThunk);
+}
+
+void BLASTBufferQueue::processNextBufferLocked() {
+    ATRACE_CALL();
+    if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) {
+        return;
+    }
+
+    if (mSurfaceControl == nullptr) {
+        ALOGE("ERROR : surface control is null");
+        return;
+    }
+
+    SurfaceComposerClient::Transaction localTransaction;
+    bool applyTransaction = true;
+    SurfaceComposerClient::Transaction* t = &localTransaction;
+    if (mNextTransaction != nullptr) {
+        t = mNextTransaction;
+        mNextTransaction = nullptr;
+        applyTransaction = false;
+    }
+
+    BufferItem bufferItem;
+
+    status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
+    if (status != OK) {
+        return;
+    }
+    auto buffer = bufferItem.mGraphicBuffer;
+    mNumFrameAvailable--;
+
+    if (buffer == nullptr) {
+        mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+        return;
+    }
+
+    mNumAcquired++;
+    mSubmitted.push(bufferItem);
+
+    bool needsDisconnect = false;
+    mBufferItemConsumer->getConnectionEvents(bufferItem.mFrameNumber, &needsDisconnect);
+
+    // if producer disconnected before, notify SurfaceFlinger
+    if (needsDisconnect) {
+        t->notifyProducerDisconnect(mSurfaceControl);
+    }
+
+    // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
+    incStrong((void*)transactionCallbackThunk);
+
+    t->setBuffer(mSurfaceControl, buffer);
+    t->setAcquireFence(mSurfaceControl,
+                       bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
+    t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
+
+    t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight});
+    t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+    t->setTransform(mSurfaceControl, bufferItem.mTransform);
+    t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
+    t->setDesiredPresentTime(bufferItem.mTimestamp);
+
+    if (applyTransaction) {
+        t->apply();
+    }
+}
+
+Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
+    if (item.mScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
+        return GLConsumer::scaleDownCrop(item.mCrop, mWidth, mHeight);
+    }
+    return item.mCrop;
+}
+
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+    ATRACE_CALL();
+    std::unique_lock _lock{mMutex};
+
+    if (mNextTransaction != nullptr) {
+        while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS) {
+            mCallbackCV.wait(_lock);
+        }
+    }
+    // add to shadow queue
+    mNumFrameAvailable++;
+    processNextBufferLocked();
+}
+
+void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
+    std::lock_guard _lock{mMutex};
+    mNextTransaction = t;
+}
+
+} // namespace android
diff --git a/libs/gui/BufferHubConsumer.cpp b/libs/gui/BufferHubConsumer.cpp
index b5cdeb2..0ddb87e 100644
--- a/libs/gui/BufferHubConsumer.cpp
+++ b/libs/gui/BufferHubConsumer.cpp
@@ -147,6 +147,16 @@
     return INVALID_OPERATION;
 }
 
+status_t BufferHubConsumer::setFrameRate(float /*frameRate*/) {
+    ALOGE("BufferHubConsumer::setFrameRate: not implemented.");
+    return INVALID_OPERATION;
+}
+
+status_t BufferHubConsumer::getFrameRate(float* /*frameRate*/) const {
+    ALOGE("BufferHubConsumer::getFrameRate: not implemented.");
+    return INVALID_OPERATION;
+}
+
 status_t BufferHubConsumer::dumpState(const String8& /*prefix*/, String8* /*outResult*/) const {
     ALOGE("BufferHubConsumer::dumpState: not implemented.");
     return INVALID_OPERATION;
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 5fb3f0b..c1d92a2 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -43,6 +43,27 @@
     }
 }
 
+void BufferQueue::ProxyConsumerListener::onFrameDequeued(const uint64_t bufferId) {
+    sp<ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != nullptr) {
+        listener->onFrameDequeued(bufferId);
+    }
+}
+
+void BufferQueue::ProxyConsumerListener::onFrameCancelled(const uint64_t bufferId) {
+    sp<ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != nullptr) {
+        listener->onFrameCancelled(bufferId);
+    }
+}
+
+void BufferQueue::ProxyConsumerListener::onFrameDetached(const uint64_t bufferId) {
+    sp<ConsumerListener> listener(mConsumerListener.promote());
+    if (listener != nullptr) {
+        listener->onFrameDetached(bufferId);
+    }
+}
+
 void BufferQueue::ProxyConsumerListener::onFrameAvailable(
         const BufferItem& item) {
     sp<ConsumerListener> listener(mConsumerListener.promote());
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 3a7cb44..4435265 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -44,6 +44,30 @@
 
 namespace android {
 
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...)                                                                           \
+    ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGD(x, ...)                                                                           \
+    ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGI(x, ...)                                                                           \
+    ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGW(x, ...)                                                                           \
+    ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGE(x, ...)                                                                           \
+    ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+
+ConsumerListener::~ConsumerListener() = default;
+
 BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
     mCore(core),
     mSlots(core->mSlots),
@@ -269,8 +293,9 @@
 
         ATRACE_INT(mCore->mConsumerName.string(),
                 static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
         mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
         VALIDATE_CONSISTENCY();
     }
 
@@ -743,7 +768,12 @@
 status_t BufferQueueConsumer::getOccupancyHistory(bool forceFlush,
         std::vector<OccupancyTracker::Segment>* outHistory) {
     std::lock_guard<std::mutex> lock(mCore->mMutex);
+#ifndef NO_BINDER
     *outHistory = mCore->mOccupancyTracker.getSegmentHistory(forceFlush);
+#else
+    (void)forceFlush;
+    outHistory->clear();
+#endif
     return NO_ERROR;
 }
 
@@ -753,6 +783,18 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueConsumer::setFrameRate(float frameRate) {
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+    mCore->mFrameRate = frameRate;
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::getFrameRate(float* frameRate) const {
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+    *frameRate = mCore->mFrameRate;
+    return NO_ERROR;
+}
+
 status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const {
     struct passwd* pwd = getpwnam("shell");
     uid_t shellUid = pwd ? pwd->pw_uid : 0;
@@ -764,7 +806,7 @@
 
     bool denied = false;
     const uid_t uid = BufferQueueThreadState::getCallingUid();
-#ifndef __ANDROID_VNDK__
+#if !defined(__ANDROID_VNDK__) && !defined(NO_BINDER)
     // permission check can't be done for vendors as vendors have no access to
     // the PermissionController. We need to do a runtime check as well, since
     // the system variant of libgui can be loaded in a vendor process. For eg:
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 0264bd2..9e5d681 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -42,6 +42,23 @@
 
 namespace android {
 
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...)                                                                           \
+    ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGD(x, ...)                                                                           \
+    ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGI(x, ...)                                                                           \
+    ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGW(x, ...)                                                                           \
+    ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGE(x, ...)                                                                           \
+    ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+          mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+
 static String8 getUniqueName() {
     static volatile int32_t counter = 0;
     return String8::format("unnamed-%d-%d", getpid(),
@@ -54,52 +71,67 @@
     return id | counter++;
 }
 
-BufferQueueCore::BufferQueueCore() :
-    mMutex(),
-    mIsAbandoned(false),
-    mConsumerControlledByApp(false),
-    mConsumerName(getUniqueName()),
-    mConsumerListener(),
-    mConsumerUsageBits(0),
-    mConsumerIsProtected(false),
-    mConnectedApi(NO_CONNECTED_API),
-    mLinkedToDeath(),
-    mConnectedProducerListener(),
-    mBufferReleasedCbEnabled(false),
-    mSlots(),
-    mQueue(),
-    mFreeSlots(),
-    mFreeBuffers(),
-    mUnusedSlots(),
-    mActiveBuffers(),
-    mDequeueCondition(),
-    mDequeueBufferCannotBlock(false),
-    mQueueBufferCanDrop(false),
-    mLegacyBufferDrop(true),
-    mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
-    mDefaultWidth(1),
-    mDefaultHeight(1),
-    mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
-    mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
-    mMaxAcquiredBufferCount(1),
-    mMaxDequeuedBufferCount(1),
-    mBufferHasBeenQueued(false),
-    mFrameCounter(0),
-    mTransformHint(0),
-    mIsAllocating(false),
-    mIsAllocatingCondition(),
-    mAllowAllocation(true),
-    mBufferAge(0),
-    mGenerationNumber(0),
-    mAsyncMode(false),
-    mSharedBufferMode(false),
-    mAutoRefresh(false),
-    mSharedBufferSlot(INVALID_BUFFER_SLOT),
-    mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
-            HAL_DATASPACE_UNKNOWN),
-    mLastQueuedSlot(INVALID_BUFFER_SLOT),
-    mUniqueId(getUniqueId())
-{
+static status_t getProcessName(int pid, String8& name) {
+    FILE* fp = fopen(String8::format("/proc/%d/cmdline", pid), "r");
+    if (NULL != fp) {
+        const size_t size = 64;
+        char proc_name[size];
+        fgets(proc_name, size, fp);
+        fclose(fp);
+        name = proc_name;
+        return NO_ERROR;
+    }
+    return INVALID_OPERATION;
+}
+
+BufferQueueCore::BufferQueueCore()
+      : mMutex(),
+        mIsAbandoned(false),
+        mConsumerControlledByApp(false),
+        mConsumerName(getUniqueName()),
+        mConsumerListener(),
+        mConsumerUsageBits(0),
+        mConsumerIsProtected(false),
+        mConnectedApi(NO_CONNECTED_API),
+        mLinkedToDeath(),
+        mConnectedProducerListener(),
+        mBufferReleasedCbEnabled(false),
+        mSlots(),
+        mQueue(),
+        mFreeSlots(),
+        mFreeBuffers(),
+        mUnusedSlots(),
+        mActiveBuffers(),
+        mDequeueCondition(),
+        mDequeueBufferCannotBlock(false),
+        mQueueBufferCanDrop(false),
+        mLegacyBufferDrop(true),
+        mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
+        mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
+        mMaxAcquiredBufferCount(1),
+        mMaxDequeuedBufferCount(1),
+        mBufferHasBeenQueued(false),
+        mFrameCounter(0),
+        mTransformHint(0),
+        mIsAllocating(false),
+        mIsAllocatingCondition(),
+        mAllowAllocation(true),
+        mBufferAge(0),
+        mGenerationNumber(0),
+        mAsyncMode(false),
+        mSharedBufferMode(false),
+        mAutoRefresh(false),
+        mSharedBufferSlot(INVALID_BUFFER_SLOT),
+        mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+                           HAL_DATASPACE_UNKNOWN),
+        mLastQueuedSlot(INVALID_BUFFER_SLOT),
+        mUniqueId(getUniqueId()),
+        mAutoPrerotation(false),
+        mTransformHintInUse(0),
+        mFrameRate(0) {
     int numStartingBuffers = getMaxBufferCountLocked();
     for (int s = 0; s < numStartingBuffers; s++) {
         mFreeSlots.insert(s);
@@ -124,10 +156,26 @@
                             mQueueBufferCanDrop, mLegacyBufferDrop);
     outResult->appendFormat("%s  default-size=[%dx%d] default-format=%d ", prefix.string(),
                             mDefaultWidth, mDefaultHeight, mDefaultBufferFormat);
-    outResult->appendFormat("transform-hint=%02x frame-counter=%" PRIu64, mTransformHint,
-                            mFrameCounter);
+    outResult->appendFormat("%s  transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.string(),
+                            mTransformHint, mFrameCounter);
+    outResult->appendFormat("%s  mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.string(),
+                            mTransformHintInUse, mAutoPrerotation);
 
-    outResult->appendFormat("\n%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+    outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+
+    outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string());
+
+    outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi,
+                            mConsumerUsageBits);
+
+    String8 producerProcName = String8("\?\?\?");
+    String8 consumerProcName = String8("\?\?\?");
+    int32_t pid = getpid();
+    getProcessName(mConnectedPid, producerProcName);
+    getProcessName(pid, consumerProcName);
+    outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId,
+                            mConnectedPid, producerProcName.string(), pid,
+                            consumerProcName.string());
     Fifo::const_iterator current(mQueue.begin());
     while (current != mQueue.end()) {
         double timestamp = current->mTimestamp / 1e9;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 4f8eb6b..9e86838 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -44,7 +44,30 @@
 
 namespace android {
 
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...)                                                                           \
+    ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGD(x, ...)                                                                           \
+    ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGI(x, ...)                                                                           \
+    ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGW(x, ...)                                                                           \
+    ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+#define BQ_LOGE(x, ...)                                                                           \
+    ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(),            \
+          mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+          ##__VA_ARGS__)
+
 static constexpr uint32_t BQ_LAYER_COUNT = 1;
+ProducerListener::~ProducerListener() = default;
 
 BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
         bool consumerIsSurfaceFlinger) :
@@ -408,6 +431,10 @@
         if (useDefaultSize) {
             width = mCore->mDefaultWidth;
             height = mCore->mDefaultHeight;
+            if (mCore->mAutoPrerotation &&
+                (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) {
+                std::swap(width, height);
+            }
         }
 
         int found = BufferItem::INVALID_BUFFER_SLOT;
@@ -508,6 +535,12 @@
             mCore->mSharedBufferSlot = found;
             mSlots[found].mBufferState.mShared = true;
         }
+
+        if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
+            if (mCore->mConsumerListener != nullptr) {
+                mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
+            }
+        }
     } // Autolock scope
 
     if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
@@ -524,6 +557,10 @@
             if (error == NO_ERROR && !mCore->mIsAbandoned) {
                 graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                 mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+                if (mCore->mConsumerListener != nullptr) {
+                    mCore->mConsumerListener->onFrameDequeued(
+                            mSlots[*outSlot].mGraphicBuffer->getId());
+                }
             }
 
             mCore->mIsAllocating = false;
@@ -617,13 +654,16 @@
             return BAD_VALUE;
         }
 
+        listener = mCore->mConsumerListener;
+        if (listener != nullptr) {
+            listener->onFrameDetached(mSlots[slot].mGraphicBuffer->getId());
+        }
         mSlots[slot].mBufferState.detachProducer();
         mCore->mActiveBuffers.erase(slot);
         mCore->mFreeSlots.insert(slot);
         mCore->clearBufferSlotLocked(slot);
         mCore->mDequeueCondition.notify_all();
         VALIDATE_CONSISTENCY();
-        listener = mCore->mConsumerListener;
     }
 
     if (listener != nullptr) {
@@ -960,14 +1000,15 @@
 
         output->width = mCore->mDefaultWidth;
         output->height = mCore->mDefaultHeight;
-        output->transformHint = mCore->mTransformHint;
+        output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
         output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
         output->nextFrameNumber = mCore->mFrameCounter + 1;
 
         ATRACE_INT(mCore->mConsumerName.string(),
                 static_cast<int32_t>(mCore->mQueue.size()));
+#ifndef NO_BINDER
         mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
-
+#endif
         // Take a ticket for the callback functions
         callbackTicket = mNextCallbackTicket++;
 
@@ -1079,6 +1120,9 @@
         mCore->mFreeBuffers.push_back(slot);
     }
 
+    if (mCore->mConsumerListener != nullptr) {
+        mCore->mConsumerListener->onFrameCancelled(mSlots[slot].mGraphicBuffer->getId());
+    }
     mSlots[slot].mFence = fence;
     mCore->mDequeueCondition.notify_all();
     VALIDATE_CONSISTENCY();
@@ -1141,9 +1185,6 @@
         case NATIVE_WINDOW_CONSUMER_IS_PROTECTED:
             value = static_cast<int32_t>(mCore->mConsumerIsProtected);
             break;
-        case NATIVE_WINDOW_MAX_BUFFER_COUNT:
-            value = static_cast<int32_t>(mCore->mMaxBufferCount);
-            break;
         default:
             return BAD_VALUE;
     }
@@ -1203,15 +1244,17 @@
 
             output->width = mCore->mDefaultWidth;
             output->height = mCore->mDefaultHeight;
-            output->transformHint = mCore->mTransformHint;
+            output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
             output->numPendingBuffers =
                     static_cast<uint32_t>(mCore->mQueue.size());
             output->nextFrameNumber = mCore->mFrameCounter + 1;
             output->bufferReplaced = false;
+            output->maxBufferCount = mCore->mMaxBufferCount;
 
             if (listener != nullptr) {
                 // Set up a death notification so that we can disconnect
                 // automatically if the remote producer dies
+#ifndef NO_BINDER
                 if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
                     status = IInterface::asBinder(listener)->linkToDeath(
                             static_cast<IBinder::DeathRecipient*>(this));
@@ -1221,6 +1264,7 @@
                     }
                     mCore->mLinkedToDeath = listener;
                 }
+#endif
                 mCore->mConnectedProducerListener = listener;
                 mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify();
             }
@@ -1289,6 +1333,7 @@
                 if (mCore->mConnectedApi == api) {
                     mCore->freeAllBuffersLocked();
 
+#ifndef NO_BINDER
                     // Remove our death notification callback if we have one
                     if (mCore->mLinkedToDeath != nullptr) {
                         sp<IBinder> token =
@@ -1298,6 +1343,7 @@
                         token->unlinkToDeath(
                                 static_cast<IBinder::DeathRecipient*>(this));
                     }
+#endif
                     mCore->mSharedBufferSlot =
                             BufferQueueCore::INVALID_BUFFER_SLOT;
                     mCore->mLinkedToDeath = nullptr;
@@ -1306,6 +1352,7 @@
                     mCore->mConnectedPid = -1;
                     mCore->mSidebandStream.clear();
                     mCore->mDequeueCondition.notify_all();
+                    mCore->mAutoPrerotation = false;
                     listener = mCore->mConsumerListener;
                 } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
                     BQ_LOGE("disconnect: not connected (req=%d)", api);
@@ -1349,6 +1396,8 @@
 void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
         PixelFormat format, uint64_t usage) {
     ATRACE_CALL();
+
+    const bool useDefaultSize = !width && !height;
     while (true) {
         size_t newBufferCount = 0;
         uint32_t allocWidth = 0;
@@ -1375,6 +1424,11 @@
 
             allocWidth = width > 0 ? width : mCore->mDefaultWidth;
             allocHeight = height > 0 ? height : mCore->mDefaultHeight;
+            if (useDefaultSize && mCore->mAutoPrerotation &&
+                (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) {
+                std::swap(allocWidth, allocHeight);
+            }
+
             allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
             allocUsage = usage | mCore->mConsumerUsageBits;
             allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size());
@@ -1405,6 +1459,11 @@
             std::unique_lock<std::mutex> lock(mCore->mMutex);
             uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
             uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
+            if (useDefaultSize && mCore->mAutoPrerotation &&
+                (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) {
+                std::swap(checkWidth, checkHeight);
+            }
+
             PixelFormat checkFormat = format != 0 ?
                     format : mCore->mDefaultBufferFormat;
             uint64_t checkUsage = usage | mCore->mConsumerUsageBits;
@@ -1607,4 +1666,24 @@
     return NO_ERROR;
 }
 
+status_t BufferQueueProducer::setAutoPrerotation(bool autoPrerotation) {
+    ATRACE_CALL();
+    BQ_LOGV("setAutoPrerotation: %d", autoPrerotation);
+
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+    mCore->mAutoPrerotation = autoPrerotation;
+    return NO_ERROR;
+}
+
+status_t BufferQueueProducer::setFrameRate(float frameRate) {
+    ATRACE_CALL();
+    BQ_LOGV("setFrameRate: %.0f", frameRate);
+
+    std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+    mCore->mFrameRate = frameRate;
+    return NO_ERROR;
+}
+
 } // namespace android
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp
index c13030b..976c9b9 100644
--- a/libs/gui/BufferQueueThreadState.cpp
+++ b/libs/gui/BufferQueueThreadState.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
+#ifndef NO_BINDER
 #include <binder/IPCThreadState.h>
 #include <binderthreadstate/CallerUtils.h>
+#endif // NO_BINDER
 #include <hwbinder/IPCThreadState.h>
 #include <private/gui/BufferQueueThreadState.h>
 #include <unistd.h>
@@ -23,17 +25,25 @@
 namespace android {
 
 uid_t BufferQueueThreadState::getCallingUid() {
+#ifndef NO_BINDER
     if (getCurrentServingCall() == BinderCallType::HWBINDER) {
         return hardware::IPCThreadState::self()->getCallingUid();
     }
     return IPCThreadState::self()->getCallingUid();
+#else // NO_BINDER
+    return hardware::IPCThreadState::self()->getCallingUid();
+#endif // NO_BINDER
 }
 
 pid_t BufferQueueThreadState::getCallingPid() {
+#ifndef NO_BINDER
     if (getCurrentServingCall() == BinderCallType::HWBINDER) {
         return hardware::IPCThreadState::self()->getCallingPid();
     }
     return IPCThreadState::self()->getCallingPid();
+#else // NO_BINDER
+    return hardware::IPCThreadState::self()->getCallingPid();
+#endif // NO_BINDER
 }
 
 } // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index abd9921..515f45c 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -101,6 +101,48 @@
     mSlots[slotIndex].mFrameNumber = 0;
 }
 
+void ConsumerBase::onFrameDequeued(const uint64_t bufferId) {
+    CB_LOGV("onFrameDequeued");
+
+    sp<FrameAvailableListener> listener;
+    {
+        Mutex::Autolock lock(mFrameAvailableMutex);
+        listener = mFrameAvailableListener.promote();
+    }
+
+    if (listener != nullptr) {
+        listener->onFrameDequeued(bufferId);
+    }
+}
+
+void ConsumerBase::onFrameCancelled(const uint64_t bufferId) {
+    CB_LOGV("onFrameCancelled");
+
+    sp<FrameAvailableListener> listener;
+    {
+        Mutex::Autolock lock(mFrameAvailableMutex);
+        listener = mFrameAvailableListener.promote();
+    }
+
+    if (listener != nullptr) {
+        listener->onFrameCancelled(bufferId);
+    }
+}
+
+void ConsumerBase::onFrameDetached(const uint64_t bufferId) {
+    CB_LOGV("onFrameDetached");
+
+    sp<FrameAvailableListener> listener;
+    {
+        Mutex::Autolock lock(mFrameAvailableMutex);
+        listener = mFrameAvailableListener.promote();
+    }
+
+    if (listener != nullptr) {
+        listener->onFrameDetached(bufferId);
+    }
+}
+
 void ConsumerBase::onFrameAvailable(const BufferItem& item) {
     CB_LOGV("onFrameAvailable");
 
@@ -321,6 +363,24 @@
     return OK;
 }
 
+status_t ConsumerBase::setFrameRate(float frameRate) {
+    Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CB_LOGE("setFrameRate: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
+    return mConsumer->setFrameRate(frameRate);
+}
+
+status_t ConsumerBase::getFrameRate(float* frameRate) {
+    Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        CB_LOGE("getFrameRate: ConsumerBase is abandoned!");
+        return NO_INIT;
+    }
+    return mConsumer->getFrameRate(frameRate);
+}
+
 void ConsumerBase::dumpState(String8& result) const {
     dumpState(result, "");
 }
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
new file mode 100644
index 0000000..15f966d
--- /dev/null
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DisplayEventDispatcher"
+
+#include <cinttypes>
+#include <cstdint>
+
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+#include <utils/Timers.h>
+
+namespace android {
+
+// Number of events to read at a time from the DisplayEventDispatcher pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
+                                               ISurfaceComposer::VsyncSource vsyncSource,
+                                               ISurfaceComposer::ConfigChanged configChanged)
+      : mLooper(looper),
+        mReceiver(vsyncSource, configChanged),
+        mWaitingForVsync(false),
+        mConfigChangeFlag(configChanged) {
+    ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
+}
+
+status_t DisplayEventDispatcher::initialize() {
+    status_t result = mReceiver.initCheck();
+    if (result) {
+        ALOGW("Failed to initialize display event receiver, status=%d", result);
+        return result;
+    }
+
+    if (mLooper != nullptr) {
+        int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
+        if (rc < 0) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    return OK;
+}
+
+void DisplayEventDispatcher::dispose() {
+    ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
+
+    if (!mReceiver.initCheck() && mLooper != nullptr) {
+        mLooper->removeFd(mReceiver.getFd());
+    }
+}
+
+status_t DisplayEventDispatcher::scheduleVsync() {
+    if (!mWaitingForVsync) {
+        ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+
+        // Drain all pending events.
+        nsecs_t vsyncTimestamp;
+        PhysicalDisplayId vsyncDisplayId;
+        uint32_t vsyncCount;
+        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+            ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+                  ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+        }
+
+        status_t status = mReceiver.requestNextVsync();
+        if (status) {
+            ALOGW("Failed to request next vsync, status=%d", status);
+            return status;
+        }
+
+        mWaitingForVsync = true;
+    }
+    return OK;
+}
+
+void DisplayEventDispatcher::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
+    if (mConfigChangeFlag == configChangeFlag) {
+        return;
+    }
+    status_t status = mReceiver.toggleConfigEvents(configChangeFlag);
+    if (status) {
+        ALOGW("Failed enable config events, status=%d", status);
+        return;
+    }
+    mConfigChangeFlag = configChangeFlag;
+}
+
+int DisplayEventDispatcher::getFd() const {
+    return mReceiver.getFd();
+}
+
+int DisplayEventDispatcher::handleEvent(int, int events, void*) {
+    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+        ALOGE("Display event receiver pipe was closed or an error occurred.  "
+              "events=0x%x",
+              events);
+        return 0; // remove the callback
+    }
+
+    if (!(events & Looper::EVENT_INPUT)) {
+        ALOGW("Received spurious callback for unhandled poll event.  "
+              "events=0x%x",
+              events);
+        return 1; // keep the callback
+    }
+
+    // Drain all pending events, keep the last vsync.
+    nsecs_t vsyncTimestamp;
+    PhysicalDisplayId vsyncDisplayId;
+    uint32_t vsyncCount;
+    if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+        ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
+              ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
+              this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+        mWaitingForVsync = false;
+        dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+    }
+
+    return 1; // keep the callback
+}
+
+bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
+                                                  PhysicalDisplayId* outDisplayId,
+                                                  uint32_t* outCount) {
+    bool gotVsync = false;
+    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+    ssize_t n;
+    while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+        ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+        for (ssize_t i = 0; i < n; i++) {
+            const DisplayEventReceiver::Event& ev = buf[i];
+            switch (ev.header.type) {
+                case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                    // Later vsync events will just overwrite the info from earlier
+                    // ones. That's fine, we only care about the most recent.
+                    gotVsync = true;
+                    *outTimestamp = ev.header.timestamp;
+                    *outDisplayId = ev.header.displayId;
+                    *outCount = ev.vsync.count;
+                    break;
+                case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+                    dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
+                    break;
+                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+                    dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
+                                          ev.config.configId, ev.config.vsyncPeriod);
+                    break;
+                default:
+                    ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
+                    break;
+            }
+        }
+    }
+    if (n < 0) {
+        ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
+    }
+    return gotVsync;
+}
+} // namespace android
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index b8faa2d..fd6aaf8 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -79,6 +79,14 @@
     return NO_INIT;
 }
 
+status_t DisplayEventReceiver::toggleConfigEvents(
+        ISurfaceComposer::ConfigChanged configChangeFlag) {
+    if (mEventConnection != nullptr) {
+        mEventConnection->toggleConfigEvents(configChangeFlag);
+        return NO_ERROR;
+    }
+    return NO_INIT;
+}
 
 ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
         size_t count) {
diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp
index 3215eca..e2ea3f9 100644
--- a/libs/gui/FrameTimestamps.cpp
+++ b/libs/gui/FrameTimestamps.cpp
@@ -364,6 +364,10 @@
     mProducerWantsEvents = false;
 }
 
+void ConsumerFrameEventHistory::setProducerWantsEvents() {
+    mProducerWantsEvents = true;
+}
+
 void ConsumerFrameEventHistory::initializeCompositorTiming(
         const CompositorTiming& compositorTiming) {
     mCompositorTiming = compositorTiming;
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 8199c98..59f1bcd 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -46,7 +46,6 @@
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
 #define EGL_PROTECTED_CONTENT_EXT 0x32C0
 
diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp
index add3ef0..058cd9a 100644
--- a/libs/gui/HdrMetadata.cpp
+++ b/libs/gui/HdrMetadata.cpp
@@ -28,8 +28,8 @@
         size += sizeof(cta8613);
     }
     if (validTypes & HDR10PLUS) {
-        size += sizeof(size_t);
-        size += hdr10plus.size();
+        size += sizeof(uint32_t);
+        size += hdr10plus.size() * sizeof(hdr10plus[0]);
     }
     return size;
 }
@@ -47,10 +47,11 @@
         FlattenableUtils::write(buffer, size, cta8613);
     }
     if (validTypes & HDR10PLUS) {
-        size_t metadataSize = hdr10plus.size();
+        uint32_t metadataSize = hdr10plus.size();
         FlattenableUtils::write(buffer, size, metadataSize);
-        memcpy(buffer, hdr10plus.data(), metadataSize);
-        FlattenableUtils::advance(buffer, size, metadataSize);
+        size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+        memcpy(buffer, hdr10plus.data(), metadataSizeinByte);
+        FlattenableUtils::advance(buffer, size, metadataSizeinByte);
     }
 
     return NO_ERROR;
@@ -74,20 +75,21 @@
         FlattenableUtils::read(buffer, size, cta8613);
     }
     if (validTypes & HDR10PLUS) {
-        if (size < sizeof(size_t)) {
+        if (size < sizeof(uint32_t)) {
             return NO_MEMORY;
         }
 
-        size_t metadataSize;
+        uint32_t metadataSize;
         FlattenableUtils::read(buffer, size, metadataSize);
 
-        if (size < metadataSize) {
+        size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]);
+        if (size < metadataSizeinByte) {
             return NO_MEMORY;
         }
 
         hdr10plus.resize(metadataSize);
-        memcpy(hdr10plus.data(), buffer, metadataSize);
-        FlattenableUtils::advance(buffer, size, metadataSize);
+        memcpy(hdr10plus.data(), buffer, metadataSizeinByte);
+        FlattenableUtils::advance(buffer, size, metadataSizeinByte);
     }
 
     return NO_ERROR;
diff --git a/libs/gui/IConsumerListener.cpp b/libs/gui/IConsumerListener.cpp
index 85ac304..f3bd90c 100644
--- a/libs/gui/IConsumerListener.cpp
+++ b/libs/gui/IConsumerListener.cpp
@@ -28,7 +28,10 @@
     ON_FRAME_REPLACED,
     ON_BUFFERS_RELEASED,
     ON_SIDEBAND_STREAM_CHANGED,
-    LAST = ON_SIDEBAND_STREAM_CHANGED,
+    ON_FRAME_DEQUEUED,
+    ON_FRAME_CANCELLED,
+    ON_FRAME_DETACHED,
+    LAST = ON_FRAME_DETACHED,
 };
 
 } // Anonymous namespace
@@ -44,6 +47,21 @@
         callRemoteAsync<decltype(&IConsumerListener::onDisconnect)>(Tag::ON_DISCONNECT);
     }
 
+    void onFrameDequeued(const uint64_t bufferId) override {
+        callRemoteAsync<decltype(&IConsumerListener::onFrameDequeued)>(Tag::ON_FRAME_DEQUEUED,
+                                                                       bufferId);
+    }
+
+    void onFrameDetached(const uint64_t bufferId) override {
+        callRemoteAsync<decltype(&IConsumerListener::onFrameDetached)>(Tag::ON_FRAME_DETACHED,
+                                                                       bufferId);
+    }
+
+    void onFrameCancelled(const uint64_t bufferId) override {
+        callRemoteAsync<decltype(&IConsumerListener::onFrameCancelled)>(Tag::ON_FRAME_CANCELLED,
+                                                                        bufferId);
+    }
+
     void onFrameAvailable(const BufferItem& item) override {
         callRemoteAsync<decltype(&IConsumerListener::onFrameAvailable)>(Tag::ON_FRAME_AVAILABLE,
                                                                         item);
@@ -72,7 +90,6 @@
 // Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
 // clang warning -Wweak-vtables)
 BpConsumerListener::~BpConsumerListener() = default;
-ConsumerListener::~ConsumerListener() = default;
 
 IMPLEMENT_META_INTERFACE(ConsumerListener, "android.gui.IConsumerListener");
 
@@ -93,6 +110,12 @@
             return callLocalAsync(data, reply, &IConsumerListener::onBuffersReleased);
         case Tag::ON_SIDEBAND_STREAM_CHANGED:
             return callLocalAsync(data, reply, &IConsumerListener::onSidebandStreamChanged);
+        case Tag::ON_FRAME_DEQUEUED:
+            return callLocalAsync(data, reply, &IConsumerListener::onFrameDequeued);
+        case Tag::ON_FRAME_CANCELLED:
+            return callLocalAsync(data, reply, &IConsumerListener::onFrameCancelled);
+        case Tag::ON_FRAME_DETACHED:
+            return callLocalAsync(data, reply, &IConsumerListener::onFrameDetached);
     }
 }
 
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index c0e246f..dda5acf 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -26,7 +26,8 @@
     STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
     SET_VSYNC_RATE,
     REQUEST_NEXT_VSYNC,
-    LAST = REQUEST_NEXT_VSYNC,
+    TOGGLE_CONFIG_EVENTS,
+    LAST = TOGGLE_CONFIG_EVENTS,
 };
 
 } // Anonymous namespace
@@ -53,6 +54,12 @@
         callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
                 Tag::REQUEST_NEXT_VSYNC);
     }
+
+    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override {
+        callRemoteAsync<decltype(
+                &IDisplayEventConnection::toggleConfigEvents)>(Tag::TOGGLE_CONFIG_EVENTS,
+                                                               configChangeFlag);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
@@ -74,6 +81,8 @@
             return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
         case Tag::REQUEST_NEXT_VSYNC:
             return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
+        case Tag::TOGGLE_CONFIG_EVENTS:
+            return callLocalAsync(data, reply, &IDisplayEventConnection::toggleConfigEvents);
     }
 }
 
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index c705d39..2521a7c 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -51,6 +51,8 @@
     GET_SIDEBAND_STREAM,
     GET_OCCUPANCY_HISTORY,
     DISCARD_FREE_BUFFERS,
+    SET_FRAME_RATE,
+    GET_FRAME_RATE,
     DUMP_STATE,
     LAST = DUMP_STATE,
 };
@@ -163,6 +165,16 @@
                 Tag::DISCARD_FREE_BUFFERS);
     }
 
+    status_t setFrameRate(float frameRate) override {
+        using Signature = decltype(&IGraphicBufferConsumer::setFrameRate);
+        return callRemote<Signature>(Tag::SET_FRAME_RATE, frameRate);
+    }
+
+    status_t getFrameRate(float* frameRate) const override {
+        using Signature = decltype(&IGraphicBufferConsumer::getFrameRate);
+        return callRemote<Signature>(Tag::GET_FRAME_RATE, frameRate);
+    }
+
     status_t dumpState(const String8& prefix, String8* outResult) const override {
         using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
         return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult);
@@ -220,6 +232,10 @@
             return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory);
         case Tag::DISCARD_FREE_BUFFERS:
             return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers);
+        case Tag::SET_FRAME_RATE:
+            return callLocal(data, reply, &IGraphicBufferConsumer::setFrameRate);
+        case Tag::GET_FRAME_RATE:
+            return callLocal(data, reply, &IGraphicBufferConsumer::getFrameRate);
         case Tag::DUMP_STATE: {
             using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
             return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 0e03b7d..7b5596e 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -73,6 +73,8 @@
     GET_UNIQUE_ID,
     GET_CONSUMER_USAGE,
     SET_LEGACY_BUFFER_DROP,
+    SET_AUTO_PREROTATION,
+    SET_FRAME_RATE,
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -547,6 +549,25 @@
         }
         return actualResult;
     }
+
+    virtual status_t setAutoPrerotation(bool autoPrerotation) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeBool(autoPrerotation);
+        status_t result = remote()->transact(SET_AUTO_PREROTATION, data, &reply);
+        if (result == NO_ERROR) {
+            result = reply.readInt32();
+        }
+        return result;
+    }
+
+    virtual status_t setFrameRate(float frameRate) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeFloat(frameRate);
+        status_t result = remote()->transact(SET_FRAME_RATE, data, &reply, IBinder::FLAG_ONEWAY);
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -675,6 +696,12 @@
     status_t getConsumerUsage(uint64_t* outUsage) const override {
         return mBase->getConsumerUsage(outUsage);
     }
+
+    status_t setAutoPrerotation(bool autoPrerotation) override {
+        return mBase->setAutoPrerotation(autoPrerotation);
+    }
+
+    status_t setFrameRate(float frameRate) override { return mBase->setFrameRate(frameRate); }
 };
 
 IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer,
@@ -688,6 +715,18 @@
     return INVALID_OPERATION;
 }
 
+status_t IGraphicBufferProducer::setAutoPrerotation(bool autoPrerotation) {
+    // No-op for IGBP other than BufferQueue.
+    (void)autoPrerotation;
+    return INVALID_OPERATION;
+}
+
+status_t IGraphicBufferProducer::setFrameRate(float frameRate) {
+    // No-op for IGBP other than BufferQueue.
+    (void)frameRate;
+    return INVALID_OPERATION;
+}
+
 status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) {
     status_t res = OK;
     res = parcel->writeUint32(USE_BUFFER_QUEUE);
@@ -1050,6 +1089,20 @@
             reply->writeInt32(result);
             return NO_ERROR;
         }
+        case SET_AUTO_PREROTATION: {
+            CHECK_INTERFACE(IGraphicBuffer, data, reply);
+            bool autoPrerotation = data.readBool();
+            status_t result = setAutoPrerotation(autoPrerotation);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
+        case SET_FRAME_RATE: {
+            CHECK_INTERFACE(IGraphicBuffer, data, reply);
+            float frameRate = data.readFloat();
+            status_t result = setFrameRate(frameRate);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
@@ -1060,135 +1113,5 @@
     parcel.read(*this);
 }
 
-constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
-    return sizeof(timestamp) +
-            sizeof(isAutoTimestamp) +
-            sizeof(dataSpace) +
-            sizeof(crop) +
-            sizeof(scalingMode) +
-            sizeof(transform) +
-            sizeof(stickyTransform) +
-            sizeof(getFrameTimestamps);
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
-    return minFlattenedSize() +
-            fence->getFlattenedSize() +
-            surfaceDamage.getFlattenedSize() +
-            hdrMetadata.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
-    return fence->getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::flatten(
-        void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, timestamp);
-    FlattenableUtils::write(buffer, size, isAutoTimestamp);
-    FlattenableUtils::write(buffer, size, dataSpace);
-    FlattenableUtils::write(buffer, size, crop);
-    FlattenableUtils::write(buffer, size, scalingMode);
-    FlattenableUtils::write(buffer, size, transform);
-    FlattenableUtils::write(buffer, size, stickyTransform);
-    FlattenableUtils::write(buffer, size, getFrameTimestamps);
-
-    status_t result = fence->flatten(buffer, size, fds, count);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    result = surfaceDamage.flatten(buffer, size);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
-    return hdrMetadata.flatten(buffer, size);
-}
-
-status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
-        void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
-    if (size < minFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, timestamp);
-    FlattenableUtils::read(buffer, size, isAutoTimestamp);
-    FlattenableUtils::read(buffer, size, dataSpace);
-    FlattenableUtils::read(buffer, size, crop);
-    FlattenableUtils::read(buffer, size, scalingMode);
-    FlattenableUtils::read(buffer, size, transform);
-    FlattenableUtils::read(buffer, size, stickyTransform);
-    FlattenableUtils::read(buffer, size, getFrameTimestamps);
-
-    fence = new Fence();
-    status_t result = fence->unflatten(buffer, size, fds, count);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    result = surfaceDamage.unflatten(buffer, size);
-    if (result != NO_ERROR) {
-        return result;
-    }
-    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
-    return hdrMetadata.unflatten(buffer, size);
-}
-
-// ----------------------------------------------------------------------------
-constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
-    return sizeof(width) +
-            sizeof(height) +
-            sizeof(transformHint) +
-            sizeof(numPendingBuffers) +
-            sizeof(nextFrameNumber) +
-            sizeof(bufferReplaced);
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
-    return minFlattenedSize() + frameTimestamps.getFlattenedSize();
-}
-
-size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
-    return frameTimestamps.getFdCount();
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
-        void*& buffer, size_t& size, int*& fds, size_t& count) const
-{
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::write(buffer, size, width);
-    FlattenableUtils::write(buffer, size, height);
-    FlattenableUtils::write(buffer, size, transformHint);
-    FlattenableUtils::write(buffer, size, numPendingBuffers);
-    FlattenableUtils::write(buffer, size, nextFrameNumber);
-    FlattenableUtils::write(buffer, size, bufferReplaced);
-
-    return frameTimestamps.flatten(buffer, size, fds, count);
-}
-
-status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
-        void const*& buffer, size_t& size, int const*& fds, size_t& count)
-{
-    if (size < minFlattenedSize()) {
-        return NO_MEMORY;
-    }
-
-    FlattenableUtils::read(buffer, size, width);
-    FlattenableUtils::read(buffer, size, height);
-    FlattenableUtils::read(buffer, size, transformHint);
-    FlattenableUtils::read(buffer, size, numPendingBuffers);
-    FlattenableUtils::read(buffer, size, nextFrameNumber);
-    FlattenableUtils::read(buffer, size, bufferReplaced);
-
-    return frameTimestamps.unflatten(buffer, size, fds, count);
-}
 
 }; // namespace android
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 808e336..5c81b9d 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -119,8 +119,6 @@
     return BBinder::onTransact(code, data, reply, flags);
 }
 
-ProducerListener::~ProducerListener() = default;
-
 DummyProducerListener::~DummyProducerListener() = default;
 
 bool BnProducerListener::needsReleaseNotify() {
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 12deaf0..2f27fd2 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -34,8 +34,10 @@
 
 #include <system/graphics.h>
 
+#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/HdrCapabilities.h>
 
 #include <utils/Log.h>
@@ -69,7 +71,7 @@
                                      const sp<IBinder>& applyToken,
                                      const InputWindowCommands& commands,
                                      int64_t desiredPresentTime,
-                                     const client_cache_t& uncacheBuffer,
+                                     const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                                      const std::vector<ListenerCallbacks>& listenerCallbacks) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -90,10 +92,11 @@
         data.writeInt64(desiredPresentTime);
         data.writeStrongBinder(uncacheBuffer.token.promote());
         data.writeUint64(uncacheBuffer.id);
+        data.writeBool(hasListenerCallbacks);
 
         if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) {
             for (const auto& [listener, callbackIds] : listenerCallbacks) {
-                data.writeStrongBinder(IInterface::asBinder(listener));
+                data.writeStrongBinder(listener);
                 data.writeInt64Vector(callbackIds);
             }
         }
@@ -109,10 +112,10 @@
     }
 
     virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
-                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
+                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   ISurfaceComposer::Rotation rotation, bool captureSecureLayers) {
+                                   ui::Rotation rotation, bool captureSecureLayers) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
@@ -350,22 +353,43 @@
         remote()->transact(BnSurfaceComposer::SET_POWER_MODE, data, &reply);
     }
 
-    virtual status_t getDisplayConfigs(const sp<IBinder>& display,
-            Vector<DisplayInfo>* configs)
-    {
+    virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState* state) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+        remote()->transact(BnSurfaceComposer::GET_DISPLAY_STATE, data, &reply);
+        const status_t result = reply.readInt32();
+        if (result == NO_ERROR) {
+            memcpy(state, reply.readInplace(sizeof(ui::DisplayState)), sizeof(ui::DisplayState));
+        }
+        return result;
+    }
+
+    virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        data.writeStrongBinder(display);
+        remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply);
+        const status_t result = reply.readInt32();
+        if (result == NO_ERROR) {
+            memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo));
+        }
+        return result;
+    }
+
+    virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
         remote()->transact(BnSurfaceComposer::GET_DISPLAY_CONFIGS, data, &reply);
-        status_t result = reply.readInt32();
+        const status_t result = reply.readInt32();
         if (result == NO_ERROR) {
-            size_t numConfigs = reply.readUint32();
+            const size_t numConfigs = reply.readUint32();
             configs->clear();
             configs->resize(numConfigs);
             for (size_t c = 0; c < numConfigs; ++c) {
-                memcpy(&(configs->editItemAt(c)),
-                        reply.readInplace(sizeof(DisplayInfo)),
-                        sizeof(DisplayInfo));
+                memcpy(&(configs->editItemAt(c)), reply.readInplace(sizeof(DisplayConfig)),
+                       sizeof(DisplayConfig));
             }
         }
         return result;
@@ -396,32 +420,6 @@
         return reply.readInt32();
     }
 
-    virtual status_t setActiveConfig(const sp<IBinder>& display, int id)
-    {
-        Parcel 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();
-    }
-
     virtual status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ColorMode>* outColorModes) {
         Parcel data, reply;
@@ -524,6 +522,88 @@
         return static_cast<status_t>(reply.readInt32());
     }
 
+    virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+                                                  bool* outSupport) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
+                                    &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
+            return result;
+        }
+        return reply.readBool(outSupport);
+    }
+
+    virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to writeInterfaceToken: %d", result);
+            return;
+        }
+
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to writeStrongBinder: %d", result);
+            return;
+        }
+        result = data.writeBool(on);
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to writeBool: %d", result);
+            return;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setAutoLowLatencyMode failed to transact: %d", result);
+            return;
+        }
+    }
+
+    virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const {
+        Parcel data, reply;
+        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        status_t result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
+            return result;
+        }
+        result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("getGameContentTypeSupport failed to transact: %d", result);
+            return result;
+        }
+        return reply.readBool(outSupport);
+    }
+
+    virtual void setGameContentType(const sp<IBinder>& display, bool on) {
+        Parcel data, reply;
+        status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to writeInterfaceToken: %d", result);
+            return;
+        }
+        result = data.writeStrongBinder(display);
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to writeStrongBinder: %d", result);
+            return;
+        }
+        result = data.writeBool(on);
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to writeBool: %d", result);
+            return;
+        }
+        result = remote()->transact(BnSurfaceComposer::SET_GAME_CONTENT_TYPE, data, &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setGameContentType failed to transact: %d", result);
+        }
+    }
+
     virtual status_t clearAnimationFrameStats() {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -851,54 +931,80 @@
         return error;
     }
 
-    virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              const std::vector<int32_t>& allowedConfigs) {
+    virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                  int32_t defaultConfig, float minRefreshRate,
+                                                  float maxRefreshRate) {
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
+            ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result);
             return result;
         }
         result = data.writeStrongBinder(displayToken);
         if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
+            ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
             return result;
         }
-        result = data.writeInt32Vector(allowedConfigs);
+        result = data.writeInt32(defaultConfig);
         if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result);
+            ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
             return result;
         }
-        result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
+        result = data.writeFloat(minRefreshRate);
         if (result != NO_ERROR) {
-            ALOGE("setAllowedDisplayConfigs failed to transact: %d", result);
+            ALOGE("setDesiredDisplayConfigSpecs failed to write minRefreshRate: %d", result);
+            return result;
+        }
+        result = data.writeFloat(maxRefreshRate);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to write maxRefreshRate: %d", result);
+            return result;
+        }
+
+        result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+                                    &reply);
+        if (result != NO_ERROR) {
+            ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result);
             return result;
         }
         return reply.readInt32();
     }
 
-    virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              std::vector<int32_t>* outAllowedConfigs) {
-        if (!outAllowedConfigs) return BAD_VALUE;
+    virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                  int32_t* outDefaultConfig,
+                                                  float* outMinRefreshRate,
+                                                  float* outMaxRefreshRate) {
+        if (!outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
         Parcel data, reply;
         status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
+            ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result);
             return result;
         }
         result = data.writeStrongBinder(displayToken);
         if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
+            ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result);
             return result;
         }
-        result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
+        result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+                                    &reply);
         if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to transact: %d", result);
+            ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
             return result;
         }
-        result = reply.readInt32Vector(outAllowedConfigs);
+        result = reply.readInt32(outDefaultConfig);
         if (result != NO_ERROR) {
-            ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result);
+            ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
+            return result;
+        }
+        result = reply.readFloat(outMinRefreshRate);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read minRefreshRate: %d", result);
+            return result;
+        }
+        result = reply.readFloat(outMaxRefreshRate);
+        if (result != NO_ERROR) {
+            ALOGE("getDesiredDisplayConfigSpecs failed to read maxRefreshRate: %d", result);
             return result;
         }
         return reply.readInt32();
@@ -977,6 +1083,35 @@
         }
         return NO_ERROR;
     }
+
+    virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+                                             float lightPosY, float lightPosZ, float lightRadius) {
+        Parcel data, reply;
+        status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (error != NO_ERROR) {
+            ALOGE("setGlobalShadowSettings: failed to write interface token: %d", error);
+            return error;
+        }
+
+        std::vector<float> shadowConfig = {ambientColor.r, ambientColor.g, ambientColor.b,
+                                           ambientColor.a, spotColor.r,    spotColor.g,
+                                           spotColor.b,    spotColor.a,    lightPosY,
+                                           lightPosZ,      lightRadius};
+
+        error = data.writeFloatVector(shadowConfig);
+        if (error != NO_ERROR) {
+            ALOGE("setGlobalShadowSettings: failed to write shadowConfig: %d", error);
+            return error;
+        }
+
+        error = remote()->transact(BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS, data, &reply,
+                                   IBinder::FLAG_ONEWAY);
+        if (error != NO_ERROR) {
+            ALOGE("setGlobalShadowSettings: failed to transact: %d", error);
+            return error;
+        }
+        return NO_ERROR;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1039,18 +1174,19 @@
             uncachedBuffer.token = data.readStrongBinder();
             uncachedBuffer.id = data.readUint64();
 
+            bool hasListenerCallbacks = data.readBool();
+
             std::vector<ListenerCallbacks> listenerCallbacks;
             int32_t listenersSize = data.readInt32();
             for (int32_t i = 0; i < listenersSize; i++) {
-                auto listener =
-                        interface_cast<ITransactionCompletedListener>(data.readStrongBinder());
+                auto listener = data.readStrongBinder();
                 std::vector<CallbackId> callbackIds;
                 data.readInt64Vector(&callbackIds);
                 listenerCallbacks.emplace_back(listener, callbackIds);
             }
-
             setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands,
-                                desiredPresentTime, uncachedBuffer, listenerCallbacks);
+                                desiredPresentTime, uncachedBuffer, hasListenerCallbacks,
+                                listenerCallbacks);
             return NO_ERROR;
         }
         case BOOT_FINISHED: {
@@ -1075,8 +1211,7 @@
             bool capturedSecureLayers = false;
             status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace,
                                          reqPixelFormat, sourceCrop, reqWidth, reqHeight,
-                                         useIdentityTransform,
-                                         static_cast<ISurfaceComposer::Rotation>(rotation),
+                                         useIdentityTransform, ui::toRotation(rotation),
                                          captureSecureLayers);
 
             reply->writeInt32(res);
@@ -1185,17 +1320,40 @@
             reply->writeStrongBinder(display);
             return NO_ERROR;
         }
+        case GET_DISPLAY_STATE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            ui::DisplayState state;
+            const sp<IBinder> display = data.readStrongBinder();
+            const status_t result = getDisplayState(display, &state);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                memcpy(reply->writeInplace(sizeof(ui::DisplayState)), &state,
+                       sizeof(ui::DisplayState));
+            }
+            return NO_ERROR;
+        }
+        case GET_DISPLAY_INFO: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            DisplayInfo info;
+            const sp<IBinder> display = data.readStrongBinder();
+            const status_t result = getDisplayInfo(display, &info);
+            reply->writeInt32(result);
+            if (result == NO_ERROR) {
+                memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo));
+            }
+            return NO_ERROR;
+        }
         case GET_DISPLAY_CONFIGS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            Vector<DisplayInfo> configs;
-            sp<IBinder> display = data.readStrongBinder();
-            status_t result = getDisplayConfigs(display, &configs);
+            Vector<DisplayConfig> configs;
+            const sp<IBinder> display = data.readStrongBinder();
+            const status_t result = getDisplayConfigs(display, &configs);
             reply->writeInt32(result);
             if (result == NO_ERROR) {
                 reply->writeUint32(static_cast<uint32_t>(configs.size()));
                 for (size_t c = 0; c < configs.size(); ++c) {
-                    memcpy(reply->writeInplace(sizeof(DisplayInfo)),
-                            &configs[c], sizeof(DisplayInfo));
+                    memcpy(reply->writeInplace(sizeof(DisplayConfig)), &configs[c],
+                           sizeof(DisplayConfig));
                 }
             }
             return NO_ERROR;
@@ -1219,14 +1377,6 @@
             reply->writeInt32(id);
             return NO_ERROR;
         }
-        case SET_ACTIVE_CONFIG: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            sp<IBinder> display = data.readStrongBinder();
-            int id = data.readInt32();
-            status_t result = setActiveConfig(display, id);
-            reply->writeInt32(result);
-            return NO_ERROR;
-        }
         case GET_DISPLAY_COLOR_MODES: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             Vector<ColorMode> colorModes;
@@ -1297,6 +1447,75 @@
             result = reply->writeInt32(result);
             return result;
         }
+
+        case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool supported = false;
+            result = getAutoLowLatencyModeSupport(display, &supported);
+            if (result == NO_ERROR) {
+                result = reply->writeBool(supported);
+            }
+            return result;
+        }
+
+        case SET_AUTO_LOW_LATENCY_MODE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("setAutoLowLatencyMode failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool setAllm = false;
+            result = data.readBool(&setAllm);
+            if (result != NO_ERROR) {
+                ALOGE("setAutoLowLatencyMode failed to readBool: %d", result);
+                return result;
+            }
+            setAutoLowLatencyMode(display, setAllm);
+            return result;
+        }
+
+        case GET_GAME_CONTENT_TYPE_SUPPORT: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool supported = false;
+            result = getGameContentTypeSupport(display, &supported);
+            if (result == NO_ERROR) {
+                result = reply->writeBool(supported);
+            }
+            return result;
+        }
+
+        case SET_GAME_CONTENT_TYPE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> display = nullptr;
+            status_t result = data.readStrongBinder(&display);
+            if (result != NO_ERROR) {
+                ALOGE("setGameContentType failed to readStrongBinder: %d", result);
+                return result;
+            }
+            bool setGameContentTypeOn = false;
+            result = data.readBool(&setGameContentTypeOn);
+            if (result != NO_ERROR) {
+                ALOGE("setGameContentType failed to readBool: %d", result);
+                return result;
+            }
+            setGameContentType(display, setGameContentTypeOn);
+            return result;
+        }
+
         case CLEAR_ANIMATION_FRAME_STATS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             status_t result = clearAnimationFrameStats();
@@ -1534,21 +1753,69 @@
             }
             return removeRegionSamplingListener(listener);
         }
-        case SET_ALLOWED_DISPLAY_CONFIGS: {
+        case SET_DESIRED_DISPLAY_CONFIG_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
-            std::vector<int32_t> allowedConfigs;
-            data.readInt32Vector(&allowedConfigs);
-            status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs);
+            int32_t defaultConfig;
+            status_t result = data.readInt32(&defaultConfig);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
+                return result;
+            }
+            float minRefreshRate;
+            result = data.readFloat(&minRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read minRefreshRate: %d", result);
+                return result;
+            }
+            float maxRefreshRate;
+            result = data.readFloat(&maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to read maxRefreshRate: %d", result);
+                return result;
+            }
+            result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, minRefreshRate,
+                                                  maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
+                      "%d",
+                      result);
+                return result;
+            }
             reply->writeInt32(result);
             return result;
         }
-        case GET_ALLOWED_DISPLAY_CONFIGS: {
+        case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
-            std::vector<int32_t> allowedConfigs;
-            status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs);
-            reply->writeInt32Vector(allowedConfigs);
+            int32_t defaultConfig;
+            float minRefreshRate;
+            float maxRefreshRate;
+
+            status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
+                                                           &minRefreshRate, &maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
+                      "%d",
+                      result);
+                return result;
+            }
+
+            result = reply->writeInt32(defaultConfig);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
+                return result;
+            }
+            result = reply->writeFloat(minRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write minRefreshRate: %d", result);
+                return result;
+            }
+            result = reply->writeFloat(maxRefreshRate);
+            if (result != NO_ERROR) {
+                ALOGE("getDesiredDisplayConfigSpecs: failed to write maxRefreshRate: %d", result);
+                return result;
+            }
             reply->writeInt32(result);
             return result;
         }
@@ -1591,6 +1858,25 @@
             }
             return notifyPowerHint(hintId);
         }
+        case SET_GLOBAL_SHADOW_SETTINGS: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+            std::vector<float> shadowConfig;
+            status_t error = data.readFloatVector(&shadowConfig);
+            if (error != NO_ERROR || shadowConfig.size() != 11) {
+                ALOGE("setGlobalShadowSettings: failed to read shadowConfig: %d", error);
+                return error;
+            }
+
+            half4 ambientColor = {shadowConfig[0], shadowConfig[1], shadowConfig[2],
+                                  shadowConfig[3]};
+            half4 spotColor = {shadowConfig[4], shadowConfig[5], shadowConfig[6], shadowConfig[7]};
+            float lightPosY = shadowConfig[8];
+            float lightPosZ = shadowConfig[9];
+            float lightRadius = shadowConfig[10];
+            return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
+                                           lightRadius);
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 129558b..621cf59 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -34,7 +34,8 @@
     CREATE_WITH_SURFACE_PARENT,
     CLEAR_LAYER_FRAME_STATS,
     GET_LAYER_FRAME_STATS,
-    LAST = GET_LAYER_FRAME_STATS,
+    MIRROR_SURFACE,
+    LAST = MIRROR_SURFACE,
 };
 
 } // Anonymous namespace
@@ -48,25 +49,28 @@
 
     status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format,
                            uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata,
-                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp) override {
+                           sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+                           uint32_t* outTransformHint) override {
         return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE,
                                                                             name, width, height,
                                                                             format, flags, parent,
                                                                             std::move(metadata),
-                                                                            handle, gbp);
+                                                                            handle, gbp,
+                                                                            outTransformHint);
     }
 
     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 {
+                                     sp<IGraphicBufferProducer>* gbp,
+                                     uint32_t* outTransformHint) override {
         return callRemote<decltype(
                 &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT,
                                                                    name, width, height, format,
                                                                    flags, parent,
-                                                                   std::move(metadata), handle,
-                                                                   gbp);
+                                                                   std::move(metadata), handle, gbp,
+                                                                   outTransformHint);
     }
 
     status_t clearLayerFrameStats(const sp<IBinder>& handle) const override {
@@ -80,6 +84,12 @@
                 &ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle,
                                                               outStats);
     }
+
+    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override {
+        return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE,
+                                                                            mirrorFromHandle,
+                                                                            outHandle);
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -105,6 +115,8 @@
             return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats);
         case Tag::GET_LAYER_FRAME_STATS:
             return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats);
+        case Tag::MIRROR_SURFACE:
+            return callLocal(data, reply, &ISurfaceComposerClient::mirrorSurface);
     }
 }
 
diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp
index 74cd4f1..69f7894 100644
--- a/libs/gui/ITransactionCompletedListener.cpp
+++ b/libs/gui/ITransactionCompletedListener.cpp
@@ -30,6 +30,66 @@
 
 } // Anonymous namespace
 
+status_t FrameEventHistoryStats::writeToParcel(Parcel* output) const {
+    status_t err = output->writeUint64(frameNumber);
+    if (err != NO_ERROR) return err;
+
+    if (gpuCompositionDoneFence) {
+        err = output->writeBool(true);
+        if (err != NO_ERROR) return err;
+
+        err = output->write(*gpuCompositionDoneFence);
+    } else {
+        err = output->writeBool(false);
+    }
+    if (err != NO_ERROR) return err;
+
+    err = output->writeInt64(compositorTiming.deadline);
+    if (err != NO_ERROR) return err;
+
+    err = output->writeInt64(compositorTiming.interval);
+    if (err != NO_ERROR) return err;
+
+    err = output->writeInt64(compositorTiming.presentLatency);
+    if (err != NO_ERROR) return err;
+
+    err = output->writeInt64(refreshStartTime);
+    if (err != NO_ERROR) return err;
+
+    err = output->writeInt64(dequeueReadyTime);
+    return err;
+}
+
+status_t FrameEventHistoryStats::readFromParcel(const Parcel* input) {
+    status_t err = input->readUint64(&frameNumber);
+    if (err != NO_ERROR) return err;
+
+    bool hasFence = false;
+    err = input->readBool(&hasFence);
+    if (err != NO_ERROR) return err;
+
+    if (hasFence) {
+        gpuCompositionDoneFence = new Fence();
+        err = input->read(*gpuCompositionDoneFence);
+        if (err != NO_ERROR) return err;
+    }
+
+    err = input->readInt64(&(compositorTiming.deadline));
+    if (err != NO_ERROR) return err;
+
+    err = input->readInt64(&(compositorTiming.interval));
+    if (err != NO_ERROR) return err;
+
+    err = input->readInt64(&(compositorTiming.presentLatency));
+    if (err != NO_ERROR) return err;
+
+    err = input->readInt64(&refreshStartTime);
+    if (err != NO_ERROR) return err;
+
+    err = input->readInt64(&dequeueReadyTime);
+    return err;
+}
+
 status_t SurfaceStats::writeToParcel(Parcel* output) const {
     status_t err = output->writeStrongBinder(surfaceControl);
     if (err != NO_ERROR) {
@@ -48,6 +108,12 @@
     } else {
         err = output->writeBool(false);
     }
+    err = output->writeUint32(transformHint);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    err = output->writeParcelable(eventStats);
     return err;
 }
 
@@ -72,7 +138,13 @@
             return err;
         }
     }
-    return NO_ERROR;
+    err = input->readUint32(&transformHint);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    err = input->readParcelable(&eventStats);
+    return err;
 }
 
 status_t TransactionStats::writeToParcel(Parcel* output) const {
@@ -151,7 +223,7 @@
     return NO_ERROR;
 }
 
-ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener,
+ListenerStats ListenerStats::createEmpty(const sp<IBinder>& listener,
                                          const std::unordered_set<CallbackId>& callbackIds) {
     ListenerStats listenerStats;
     listenerStats.listener = listener;
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 04d2871..b3eb994 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -18,6 +18,8 @@
 #include <binder/Parcel.h>
 #include <gui/LayerMetadata.h>
 
+#include "android/view/LayerMetadataKey.h"
+
 using android::base::StringPrintf;
 
 namespace android {
@@ -113,12 +115,12 @@
 
 std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
     if (!has(key)) return std::string();
-    switch (key) {
-        case METADATA_OWNER_UID:
+    switch (static_cast<view::LayerMetadataKey>(key)) {
+        case view::LayerMetadataKey::METADATA_OWNER_UID:
             return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
-        case METADATA_WINDOW_TYPE:
+        case view::LayerMetadataKey::METADATA_WINDOW_TYPE:
             return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
-        case METADATA_TASK_ID:
+        case view::LayerMetadataKey::METADATA_TASK_ID:
             return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
         default:
             return StringPrintf("%d%s%dbytes", key, separator,
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 42eb921..5547efc 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -86,7 +86,7 @@
     memcpy(output.writeInplace(16 * sizeof(float)),
            colorTransform.asArray(), 16 * sizeof(float));
     output.writeFloat(cornerRadius);
-    output.writeBool(hasListenerCallbacks);
+    output.writeUint32(backgroundBlurRadius);
     output.writeStrongBinder(cachedBuffer.token.promote());
     output.writeUint64(cachedBuffer.id);
     output.writeParcelable(metadata);
@@ -95,6 +95,24 @@
     output.writeUint32(static_cast<uint32_t>(bgColorDataspace));
     output.writeBool(colorSpaceAgnostic);
 
+    auto err = output.writeVectorSize(listeners);
+    if (err) {
+        return err;
+    }
+
+    for (auto listener : listeners) {
+        err = output.writeStrongBinder(listener.transactionCompletedListener);
+        if (err) {
+            return err;
+        }
+        err = output.writeInt64Vector(listener.callbackIds);
+        if (err) {
+            return err;
+        }
+    }
+    output.writeFloat(shadowRadius);
+    output.writeInt32(frameRateSelectionPriority);
+    output.writeFloat(frameRate);
     return NO_ERROR;
 }
 
@@ -156,7 +174,7 @@
 
     colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float))));
     cornerRadius = input.readFloat();
-    hasListenerCallbacks = input.readBool();
+    backgroundBlurRadius = input.readUint32();
     cachedBuffer.token = input.readStrongBinder();
     cachedBuffer.id = input.readUint64();
     input.readParcelable(&metadata);
@@ -165,16 +183,25 @@
     bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32());
     colorSpaceAgnostic = input.readBool();
 
+    int32_t numListeners = input.readInt32();
+    listeners.clear();
+    for (int i = 0; i < numListeners; i++) {
+        auto listener = input.readStrongBinder();
+        std::vector<CallbackId> callbackIds;
+        input.readInt64Vector(&callbackIds);
+        listeners.emplace_back(listener, callbackIds);
+    }
+    shadowRadius = input.readFloat();
+    frameRateSelectionPriority = input.readInt32();
+    frameRate = input.readFloat();
     return NO_ERROR;
 }
 
 status_t ComposerState::write(Parcel& output) const {
-    output.writeStrongBinder(IInterface::asBinder(client));
     return state.write(output);
 }
 
 status_t ComposerState::read(const Parcel& input) {
-    client = interface_cast<ISurfaceComposerClient>(input.readStrongBinder());
     return state.read(input);
 }
 
@@ -182,7 +209,6 @@
 DisplayState::DisplayState() :
     what(0),
     layerStack(0),
-    orientation(eOrientationDefault),
     viewport(Rect::EMPTY_RECT),
     frame(Rect::EMPTY_RECT),
     width(0),
@@ -194,7 +220,7 @@
     output.writeStrongBinder(IInterface::asBinder(surface));
     output.writeUint32(what);
     output.writeUint32(layerStack);
-    output.writeUint32(orientation);
+    output.writeUint32(toRotationInt(orientation));
     output.write(viewport);
     output.write(frame);
     output.writeUint32(width);
@@ -207,7 +233,7 @@
     surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
     what = input.readUint32();
     layerStack = input.readUint32();
-    orientation = input.readUint32();
+    orientation = ui::toRotation(input.readUint32());
     input.read(viewport);
     input.read(frame);
     width = input.readUint32();
@@ -267,8 +293,9 @@
     }
     if (other.what & eFlagsChanged) {
         what |= eFlagsChanged;
-        flags = other.flags;
-        mask = other.mask;
+        flags &= ~other.mask;
+        flags |= (other.flags & other.mask);
+        mask |= other.mask;
     }
     if (other.what & eLayerStackChanged) {
         what |= eLayerStackChanged;
@@ -282,6 +309,10 @@
         what |= eCornerRadiusChanged;
         cornerRadius = other.cornerRadius;
     }
+    if (other.what & eBackgroundBlurRadiusChanged) {
+        what |= eBackgroundBlurRadiusChanged;
+        backgroundBlurRadius = other.backgroundBlurRadius;
+    }
     if (other.what & eDeferTransaction_legacy) {
         what |= eDeferTransaction_legacy;
         barrierHandle_legacy = other.barrierHandle_legacy;
@@ -292,9 +323,6 @@
         what |= eOverrideScalingModeChanged;
         overrideScalingMode = other.overrideScalingMode;
     }
-    if (other.what & eGeometryAppliesWithResize) {
-        what |= eGeometryAppliesWithResize;
-    }
     if (other.what & eReparentChildren) {
         what |= eReparentChildren;
         reparentHandle = other.reparentHandle;
@@ -365,7 +393,6 @@
     }
     if (other.what & eHasListenerCallbacksChanged) {
         what |= eHasListenerCallbacksChanged;
-        hasListenerCallbacks = other.hasListenerCallbacks;
     }
 
 #ifndef NO_INPUT
@@ -389,6 +416,18 @@
         what |= eMetadataChanged;
         metadata.merge(other.metadata);
     }
+    if (other.what & eShadowRadiusChanged) {
+        what |= eShadowRadiusChanged;
+        shadowRadius = other.shadowRadius;
+    }
+    if (other.what & eFrameRateSelectionPriority) {
+        what |= eFrameRateSelectionPriority;
+        frameRateSelectionPriority = other.frameRateSelectionPriority;
+    }
+    if (other.what & eFrameRateChanged) {
+        what |= eFrameRateChanged;
+        frameRate = other.frameRate;
+    }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
               "other.what=0x%" PRIu64 " what=0x%" PRIu64,
diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp
new file mode 100644
index 0000000..30f0ef6
--- /dev/null
+++ b/libs/gui/QueueBufferInputOutput.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright 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.
+ */
+
+#include <inttypes.h>
+
+#define LOG_TAG "QueueBufferInputOutput"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() {
+    return sizeof(timestamp) +
+            sizeof(isAutoTimestamp) +
+            sizeof(dataSpace) +
+            sizeof(crop) +
+            sizeof(scalingMode) +
+            sizeof(transform) +
+            sizeof(stickyTransform) +
+            sizeof(getFrameTimestamps);
+}
+
+IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) {
+    parcel.read(*this);
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const {
+    return minFlattenedSize() +
+            fence->getFlattenedSize() +
+            surfaceDamage.getFlattenedSize() +
+            hdrMetadata.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const {
+    return fence->getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, timestamp);
+    FlattenableUtils::write(buffer, size, isAutoTimestamp);
+    FlattenableUtils::write(buffer, size, dataSpace);
+    FlattenableUtils::write(buffer, size, crop);
+    FlattenableUtils::write(buffer, size, scalingMode);
+    FlattenableUtils::write(buffer, size, transform);
+    FlattenableUtils::write(buffer, size, stickyTransform);
+    FlattenableUtils::write(buffer, size, getFrameTimestamps);
+
+    status_t result = fence->flatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    result = surfaceDamage.flatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    return hdrMetadata.flatten(buffer, size);
+}
+
+status_t IGraphicBufferProducer::QueueBufferInput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, timestamp);
+    FlattenableUtils::read(buffer, size, isAutoTimestamp);
+    FlattenableUtils::read(buffer, size, dataSpace);
+    FlattenableUtils::read(buffer, size, crop);
+    FlattenableUtils::read(buffer, size, scalingMode);
+    FlattenableUtils::read(buffer, size, transform);
+    FlattenableUtils::read(buffer, size, stickyTransform);
+    FlattenableUtils::read(buffer, size, getFrameTimestamps);
+
+    fence = new Fence();
+    status_t result = fence->unflatten(buffer, size, fds, count);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    result = surfaceDamage.unflatten(buffer, size);
+    if (result != NO_ERROR) {
+        return result;
+    }
+    FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize());
+    return hdrMetadata.unflatten(buffer, size);
+}
+
+////////////////////////////////////////////////////////////////////////
+constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() {
+    return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) +
+            sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount);
+}
+size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const {
+    return minFlattenedSize() + frameTimestamps.getFlattenedSize();
+}
+
+size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const {
+    return frameTimestamps.getFdCount();
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const
+{
+    if (size < getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, width);
+    FlattenableUtils::write(buffer, size, height);
+    FlattenableUtils::write(buffer, size, transformHint);
+    FlattenableUtils::write(buffer, size, numPendingBuffers);
+    FlattenableUtils::write(buffer, size, nextFrameNumber);
+    FlattenableUtils::write(buffer, size, bufferReplaced);
+    FlattenableUtils::write(buffer, size, maxBufferCount);
+
+    return frameTimestamps.flatten(buffer, size, fds, count);
+}
+
+status_t IGraphicBufferProducer::QueueBufferOutput::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count)
+{
+    if (size < minFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, width);
+    FlattenableUtils::read(buffer, size, height);
+    FlattenableUtils::read(buffer, size, transformHint);
+    FlattenableUtils::read(buffer, size, numPendingBuffers);
+    FlattenableUtils::read(buffer, size, nextFrameNumber);
+    FlattenableUtils::read(buffer, size, bufferReplaced);
+    FlattenableUtils::read(buffer, size, maxBufferCount);
+
+    return frameTimestamps.unflatten(buffer, size, fds, count);
+}
+
+} // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index b822319..278cc59 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -50,6 +50,17 @@
 using ui::ColorMode;
 using ui::Dataspace;
 
+namespace {
+
+bool isInterceptorRegistrationOp(int op) {
+    return op == NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR ||
+            op == NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR ||
+            op == NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR ||
+            op == NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR;
+}
+
+} // namespace
+
 Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp)
       : mGraphicBufferProducer(bufferProducer),
         mCrop(Rect::EMPTY_RECT),
@@ -97,6 +108,7 @@
     mConnectedToCpu = false;
     mProducerControlledByApp = controlledByApp;
     mSwapIntervalZero = false;
+    mMaxBufferCount = 0;
 }
 
 Surface::~Surface() {
@@ -365,18 +377,58 @@
 int Surface::hook_dequeueBuffer(ANativeWindow* window,
         ANativeWindowBuffer** buffer, int* fenceFd) {
     Surface* c = getSelf(window);
+    {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mDequeueInterceptor != nullptr) {
+            auto interceptor = c->mDequeueInterceptor;
+            auto data = c->mDequeueInterceptorData;
+            return interceptor(window, Surface::dequeueBufferInternal, data, buffer, fenceFd);
+        }
+    }
+    return c->dequeueBuffer(buffer, fenceFd);
+}
+
+int Surface::dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                   int* fenceFd) {
+    Surface* c = getSelf(window);
     return c->dequeueBuffer(buffer, fenceFd);
 }
 
 int Surface::hook_cancelBuffer(ANativeWindow* window,
         ANativeWindowBuffer* buffer, int fenceFd) {
     Surface* c = getSelf(window);
+    {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mCancelInterceptor != nullptr) {
+            auto interceptor = c->mCancelInterceptor;
+            auto data = c->mCancelInterceptorData;
+            return interceptor(window, Surface::cancelBufferInternal, data, buffer, fenceFd);
+        }
+    }
+    return c->cancelBuffer(buffer, fenceFd);
+}
+
+int Surface::cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+    Surface* c = getSelf(window);
     return c->cancelBuffer(buffer, fenceFd);
 }
 
 int Surface::hook_queueBuffer(ANativeWindow* window,
         ANativeWindowBuffer* buffer, int fenceFd) {
     Surface* c = getSelf(window);
+    {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mQueueInterceptor != nullptr) {
+            auto interceptor = c->mQueueInterceptor;
+            auto data = c->mQueueInterceptorData;
+            return interceptor(window, Surface::queueBufferInternal, data, buffer, fenceFd);
+        }
+    }
+    return c->queueBuffer(buffer, fenceFd);
+}
+
+int Surface::queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd) {
+    Surface* c = getSelf(window);
     return c->queueBuffer(buffer, fenceFd);
 }
 
@@ -419,21 +471,38 @@
     return c->queueBuffer(buffer, -1);
 }
 
-int Surface::hook_query(const ANativeWindow* window,
-                                int what, int* value) {
-    const Surface* c = getSelf(window);
-    return c->query(what, value);
-}
-
 int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
     va_list args;
     va_start(args, operation);
     Surface* c = getSelf(window);
-    int result = c->perform(operation, args);
+    int result;
+    // Don't acquire shared ownership of the interceptor mutex if we're going to
+    // do interceptor registration, as otherwise we'll deadlock on acquiring
+    // exclusive ownership.
+    if (!isInterceptorRegistrationOp(operation)) {
+        std::shared_lock<std::shared_mutex> lock(c->mInterceptorMutex);
+        if (c->mPerformInterceptor != nullptr) {
+            result = c->mPerformInterceptor(window, Surface::performInternal,
+                                            c->mPerformInterceptorData, operation, args);
+            va_end(args);
+            return result;
+        }
+    }
+    result = c->perform(operation, args);
     va_end(args);
     return result;
 }
 
+int Surface::performInternal(ANativeWindow* window, int operation, va_list args) {
+    Surface* c = getSelf(window);
+    return c->perform(operation, args);
+}
+
+int Surface::hook_query(const ANativeWindow* window, int what, int* value) {
+    const Surface* c = getSelf(window);
+    return c->query(what, value);
+}
+
 int Surface::setSwapInterval(int interval) {
     ATRACE_CALL();
     // EGL specification states:
@@ -962,6 +1031,10 @@
                 *value = static_cast<int>(mDataSpace);
                 return NO_ERROR;
             }
+            case NATIVE_WINDOW_MAX_BUFFER_COUNT: {
+                *value = mMaxBufferCount;
+                return NO_ERROR;
+            }
         }
     }
     return mGraphicBufferProducer->query(what, value);
@@ -1073,6 +1146,43 @@
     case NATIVE_WINDOW_GET_CONSUMER_USAGE64:
         res = dispatchGetConsumerUsage64(args);
         break;
+    case NATIVE_WINDOW_SET_AUTO_PREROTATION:
+        res = dispatchSetAutoPrerotation(args);
+        break;
+    case NATIVE_WINDOW_GET_LAST_DEQUEUE_START:
+        res = dispatchGetLastDequeueStartTime(args);
+        break;
+    case NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT:
+        res = dispatchSetDequeueTimeout(args);
+        break;
+    case NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION:
+        res = dispatchGetLastDequeueDuration(args);
+        break;
+    case NATIVE_WINDOW_GET_LAST_QUEUE_DURATION:
+        res = dispatchGetLastQueueDuration(args);
+        break;
+    case NATIVE_WINDOW_SET_FRAME_RATE:
+        res = dispatchSetFrameRate(args);
+        break;
+    case NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR:
+        res = dispatchAddCancelInterceptor(args);
+        break;
+    case NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR:
+        res = dispatchAddDequeueInterceptor(args);
+        break;
+    case NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR:
+        res = dispatchAddPerformInterceptor(args);
+        break;
+    case NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR:
+        res = dispatchAddQueueInterceptor(args);
+        break;
+    case NATIVE_WINDOW_ALLOCATE_BUFFERS:
+        allocateBuffers();
+        res = NO_ERROR;
+        break;
+    case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER:
+        res = dispatchGetLastQueuedBuffer(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -1273,6 +1383,102 @@
     return getConsumerUsage(usage);
 }
 
+int Surface::dispatchSetAutoPrerotation(va_list args) {
+    bool autoPrerotation = va_arg(args, int);
+    return setAutoPrerotation(autoPrerotation);
+}
+
+int Surface::dispatchGetLastDequeueStartTime(va_list args) {
+    int64_t* lastDequeueStartTime = va_arg(args, int64_t*);
+    *lastDequeueStartTime = mLastDequeueStartTime;
+    return NO_ERROR;
+}
+
+int Surface::dispatchSetDequeueTimeout(va_list args) {
+    nsecs_t timeout = va_arg(args, int64_t);
+    return setDequeueTimeout(timeout);
+}
+
+int Surface::dispatchGetLastDequeueDuration(va_list args) {
+    int64_t* lastDequeueDuration = va_arg(args, int64_t*);
+    *lastDequeueDuration = mLastDequeueDuration;
+    return NO_ERROR;
+}
+
+int Surface::dispatchGetLastQueueDuration(va_list args) {
+    int64_t* lastQueueDuration = va_arg(args, int64_t*);
+    *lastQueueDuration = mLastQueueDuration;
+    return NO_ERROR;
+}
+
+int Surface::dispatchSetFrameRate(va_list args) {
+    float frameRate = static_cast<float>(va_arg(args, double));
+    return setFrameRate(frameRate);
+}
+
+int Surface::dispatchAddCancelInterceptor(va_list args) {
+    ANativeWindow_cancelBufferInterceptor interceptor =
+            va_arg(args, ANativeWindow_cancelBufferInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mCancelInterceptor = interceptor;
+    mCancelInterceptorData = data;
+    return NO_ERROR;
+}
+
+int Surface::dispatchAddDequeueInterceptor(va_list args) {
+    ANativeWindow_dequeueBufferInterceptor interceptor =
+            va_arg(args, ANativeWindow_dequeueBufferInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mDequeueInterceptor = interceptor;
+    mDequeueInterceptorData = data;
+    return NO_ERROR;
+}
+
+int Surface::dispatchAddPerformInterceptor(va_list args) {
+    ANativeWindow_performInterceptor interceptor = va_arg(args, ANativeWindow_performInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mPerformInterceptor = interceptor;
+    mPerformInterceptorData = data;
+    return NO_ERROR;
+}
+
+int Surface::dispatchAddQueueInterceptor(va_list args) {
+    ANativeWindow_queueBufferInterceptor interceptor =
+            va_arg(args, ANativeWindow_queueBufferInterceptor);
+    void* data = va_arg(args, void*);
+    std::lock_guard<std::shared_mutex> lock(mInterceptorMutex);
+    mQueueInterceptor = interceptor;
+    mQueueInterceptorData = data;
+    return NO_ERROR;
+}
+
+int Surface::dispatchGetLastQueuedBuffer(va_list args) {
+    AHardwareBuffer** buffer = va_arg(args, AHardwareBuffer**);
+    int* fence = va_arg(args, int*);
+    float* matrix = va_arg(args, float*);
+    sp<GraphicBuffer> graphicBuffer;
+    sp<Fence> spFence;
+
+    int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix);
+
+    if (graphicBuffer != nullptr) {
+        *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get());
+        AHardwareBuffer_acquire(*buffer);
+    } else {
+        *buffer = nullptr;
+    }
+
+    if (spFence != nullptr) {
+        *fence = spFence->dup();
+    } else {
+        *fence = -1;
+    }
+    return result;
+}
+
 bool Surface::transformToDisplayInverse() {
     return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -1307,6 +1513,7 @@
         mDefaultWidth = output.width;
         mDefaultHeight = output.height;
         mNextFrameNumber = output.nextFrameNumber;
+        mMaxBufferCount = output.maxBufferCount;
 
         // Ignore transform hint if sticky transform is set or transform to display inverse flag is
         // set. Transform hint should be ignored if the client is expected to always submit buffers
@@ -1348,6 +1555,8 @@
         mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
         mTransform = 0;
         mStickyTransform = 0;
+        mAutoPrerotation = false;
+        mEnableFrameTimestamps = false;
 
         if (api == NATIVE_WINDOW_API_CPU) {
             mConnectedToCpu = false;
@@ -1934,11 +2143,6 @@
     return mGraphicBufferProducer->getConsumerUsage(outUsage);
 }
 
-nsecs_t Surface::getLastDequeueStartTime() const {
-    Mutex::Autolock lock(mMutex);
-    return mLastDequeueStartTime;
-}
-
 status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) {
     if (out == nullptr) {
         ALOGE("%s: out must not be null!", __FUNCTION__);
@@ -1982,6 +2186,24 @@
     return err;
 }
 
+int Surface::setAutoPrerotation(bool autoPrerotation) {
+    ATRACE_CALL();
+    ALOGV("Surface::setAutoPrerotation (%d)", autoPrerotation);
+    Mutex::Autolock lock(mMutex);
+
+    if (mAutoPrerotation == autoPrerotation) {
+        return OK;
+    }
+
+    status_t err = mGraphicBufferProducer->setAutoPrerotation(autoPrerotation);
+    if (err == NO_ERROR) {
+        mAutoPrerotation = autoPrerotation;
+    }
+    ALOGE_IF(err, "IGraphicBufferProducer::setAutoPrerotation(%d) returned %s", autoPrerotation,
+             strerror(-err));
+    return err;
+}
+
 void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_t>& slots) {
     ATRACE_CALL();
     sp<Surface> parent = mParent.promote();
@@ -2000,4 +2222,11 @@
     mSurfaceListener->onBuffersDiscarded(discardedBufs);
 }
 
+status_t Surface::setFrameRate(float frameRate) {
+    ATRACE_CALL();
+    ALOGV("Surface::setTargetFrameRate");
+    Mutex::Autolock lock(mMutex);
+    return mGraphicBufferProducer->setFrameRate(frameRate);
+}
+
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index def9fe9..7017b7c 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -31,8 +31,6 @@
 
 #include <system/graphics.h>
 
-#include <ui/DisplayInfo.h>
-
 #include <gui/BufferItemConsumer.h>
 #include <gui/CpuConsumer.h>
 #include <gui/IGraphicBufferProducer.h>
@@ -41,6 +39,7 @@
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
 
 #ifndef NO_INPUT
 #include <input/InputWindow.h>
@@ -189,44 +188,53 @@
 }
 
 void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
-    std::lock_guard<std::mutex> lock(mMutex);
+    std::unordered_map<CallbackId, CallbackTranslation> callbacksMap;
+    {
+        std::lock_guard<std::mutex> 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& transactionStats : listenerStats.transactionStats) {
-        for (auto callbackId : transactionStats.callbackIds) {
-            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
-            surfaceControls.insert(callbackSurfaceControls.begin(), callbackSurfaceControls.end());
+        /* 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.
+         */
+        callbacksMap = mCallbacks;
+        for (const auto& transactionStats : listenerStats.transactionStats) {
+            for (auto& callbackId : transactionStats.callbackIds) {
+                mCallbacks.erase(callbackId);
+            }
         }
     }
-
     for (const auto& transactionStats : listenerStats.transactionStats) {
         for (auto callbackId : transactionStats.callbackIds) {
-            auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId];
+            auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[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);
+                surfaceControlStats
+                        .emplace_back(callbacksMap[callbackId]
+                                              .surfaceControls[surfaceStats.surfaceControl],
+                                      transactionStats.latchTime, surfaceStats.acquireTime,
+                                      transactionStats.presentFence,
+                                      surfaceStats.previousReleaseFence, surfaceStats.transformHint,
+                                      surfaceStats.eventStats);
+                if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
+                    callbacksMap[callbackId]
+                            .surfaceControls[surfaceStats.surfaceControl]
+                            ->setTransformHint(surfaceStats.transformHint);
+                }
             }
 
             callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
                              surfaceControlStats);
-            mCallbacks.erase(callbackId);
         }
     }
 }
@@ -322,21 +330,148 @@
         mTransactionNestCount(other.mTransactionNestCount),
         mAnimation(other.mAnimation),
         mEarlyWakeup(other.mEarlyWakeup),
+        mContainsBuffer(other.mContainsBuffer),
         mDesiredPresentTime(other.mDesiredPresentTime) {
     mDisplayStates = other.mDisplayStates;
     mComposerStates = other.mComposerStates;
     mInputWindowCommands = other.mInputWindowCommands;
+    mListenerCallbacks = other.mListenerCallbacks;
+}
+
+std::unique_ptr<SurfaceComposerClient::Transaction>
+SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
+    auto transaction = std::make_unique<Transaction>();
+    if (transaction->readFromParcel(parcel) == NO_ERROR) {
+        return transaction;
+    }
+    return nullptr;
+}
+
+status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
+    const uint32_t forceSynchronous = parcel->readUint32();
+    const uint32_t transactionNestCount = parcel->readUint32();
+    const bool animation = parcel->readBool();
+    const bool earlyWakeup = parcel->readBool();
+    const bool containsBuffer = parcel->readBool();
+    const int64_t desiredPresentTime = parcel->readInt64();
+
+    size_t count = static_cast<size_t>(parcel->readUint32());
+    if (count > parcel->dataSize()) {
+        return BAD_VALUE;
+    }
+    SortedVector<DisplayState> displayStates;
+    displayStates.setCapacity(count);
+    for (size_t i = 0; i < count; i++) {
+        DisplayState displayState;
+        if (displayState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        displayStates.add(displayState);
+    }
+
+    count = static_cast<size_t>(parcel->readUint32());
+    if (count > parcel->dataSize()) {
+        return BAD_VALUE;
+    }
+    std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> listenerCallbacks;
+    listenerCallbacks.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        sp<ITransactionCompletedListener> listener =
+                interface_cast<ITransactionCompletedListener>(parcel->readStrongBinder());
+        size_t numCallbackIds = parcel->readUint32();
+        if (numCallbackIds > parcel->dataSize()) {
+            return BAD_VALUE;
+        }
+        for (size_t j = 0; j < numCallbackIds; j++) {
+            listenerCallbacks[listener].callbackIds.insert(parcel->readInt64());
+        }
+        size_t numSurfaces = parcel->readUint32();
+        if (numSurfaces > parcel->dataSize()) {
+            return BAD_VALUE;
+        }
+        for (size_t j = 0; j < numSurfaces; j++) {
+            sp<SurfaceControl> surface;
+            surface = SurfaceControl::readFromParcel(parcel);
+            listenerCallbacks[listener].surfaceControls.insert(surface);
+        }
+    }
+
+    count = static_cast<size_t>(parcel->readUint32());
+    if (count > parcel->dataSize()) {
+        return BAD_VALUE;
+    }
+    std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates;
+    composerStates.reserve(count);
+    for (size_t i = 0; i < count; i++) {
+        sp<IBinder> surfaceControlHandle = parcel->readStrongBinder();
+
+        ComposerState composerState;
+        if (composerState.read(*parcel) == BAD_VALUE) {
+            return BAD_VALUE;
+        }
+        composerStates[surfaceControlHandle] = composerState;
+    }
+
+    InputWindowCommands inputWindowCommands;
+    inputWindowCommands.read(*parcel);
+
+    // Parsing was successful. Update the object.
+    mForceSynchronous = forceSynchronous;
+    mTransactionNestCount = transactionNestCount;
+    mAnimation = animation;
+    mEarlyWakeup = earlyWakeup;
+    mContainsBuffer = containsBuffer;
+    mDesiredPresentTime = desiredPresentTime;
+    mDisplayStates = displayStates;
+    mListenerCallbacks = listenerCallbacks;
+    mComposerStates = composerStates;
+    mInputWindowCommands = inputWindowCommands;
+    return NO_ERROR;
+}
+
+status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const {
+    parcel->writeUint32(mForceSynchronous);
+    parcel->writeUint32(mTransactionNestCount);
+    parcel->writeBool(mAnimation);
+    parcel->writeBool(mEarlyWakeup);
+    parcel->writeBool(mContainsBuffer);
+    parcel->writeInt64(mDesiredPresentTime);
+    parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
+    for (auto const& displayState : mDisplayStates) {
+        displayState.write(*parcel);
+    }
+
+    parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
+    for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
+        parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener));
+        parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size()));
+        for (auto callbackId : callbackInfo.callbackIds) {
+            parcel->writeInt64(callbackId);
+        }
+        parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size()));
+        for (auto surfaceControl : callbackInfo.surfaceControls) {
+            surfaceControl->writeToParcel(parcel);
+        }
+    }
+
+    parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
+    for (auto const& [surfaceHandle, composerState] : mComposerStates) {
+        parcel->writeStrongBinder(surfaceHandle);
+        composerState.write(*parcel);
+    }
+
+    mInputWindowCommands.write(*parcel);
+    return NO_ERROR;
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
-    for (auto const& kv : other.mComposerStates) {
-        if (mComposerStates.count(kv.first) == 0) {
-            mComposerStates[kv.first] = kv.second;
+    for (auto const& [surfaceHandle, composerState] : other.mComposerStates) {
+        if (mComposerStates.count(surfaceHandle) == 0) {
+            mComposerStates[surfaceHandle] = composerState;
         } else {
-            mComposerStates[kv.first].state.merge(kv.second.state);
+            mComposerStates[surfaceHandle].state.merge(composerState.state);
         }
     }
-    other.mComposerStates.clear();
 
     for (auto const& state : other.mDisplayStates) {
         ssize_t index = mDisplayStates.indexOf(state);
@@ -346,46 +481,64 @@
             mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state);
         }
     }
-    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()));
+
+        mListenerCallbacks[listener].surfaceControls.insert(surfaceControls.begin(),
+                                                            surfaceControls.end());
+
+        auto& currentProcessCallbackInfo =
+                mListenerCallbacks[TransactionCompletedListener::getIInstance()];
+        currentProcessCallbackInfo.surfaceControls
+                .insert(std::make_move_iterator(surfaceControls.begin()),
+                        std::make_move_iterator(surfaceControls.end()));
+
+        // register all surface controls for all callbackIds for this listener that is merging
+        for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
+            TransactionCompletedListener::getInstance()
+                    ->addSurfaceControlToCallbacks(surfaceControl,
+                                                   currentProcessCallbackInfo.callbackIds);
+        }
     }
-    other.mListenerCallbacks.clear();
 
     mInputWindowCommands.merge(other.mInputWindowCommands);
-    other.mInputWindowCommands.clear();
 
     mContainsBuffer = other.mContainsBuffer;
-    other.mContainsBuffer = false;
-
     mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup;
-    other.mEarlyWakeup = false;
-
+    other.clear();
     return *this;
 }
 
-void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle,
-        const sp<ISurfaceComposerClient>& client) {
+void SurfaceComposerClient::Transaction::clear() {
+    mComposerStates.clear();
+    mDisplayStates.clear();
+    mListenerCallbacks.clear();
+    mInputWindowCommands.clear();
+    mContainsBuffer = false;
+    mForceSynchronous = 0;
+    mTransactionNestCount = 0;
+    mAnimation = false;
+    mEarlyWakeup = false;
+    mDesiredPresentTime = -1;
+}
+
+void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle) {
     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);
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, {});
+    sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, false, {});
 }
 
 void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -396,7 +549,7 @@
     uncacheBuffer.id = cacheId;
 
     sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
-    sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, {});
+    sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {});
 }
 
 void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -405,8 +558,8 @@
     }
 
     size_t count = 0;
-    for (auto& [sc, cs] : mComposerStates) {
-        layer_state_t* s = getLayerState(sc);
+    for (auto& [handle, cs] : mComposerStates) {
+        layer_state_t* s = getLayerState(handle);
         if (!(s->what & layer_state_t::eBufferChanged)) {
             continue;
         }
@@ -445,8 +598,8 @@
 
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
 
+    bool hasListenerCallbacks = !mListenerCallbacks.empty();
     std::vector<ListenerCallbacks> listenerCallbacks;
-
     // For every listener with registered callbacks
     for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
         auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -454,19 +607,24 @@
             continue;
         }
 
-        listenerCallbacks.emplace_back(listener, std::move(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;
+        if (surfaceControls.empty()) {
+            listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
+        } else {
+            // 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;
+                }
+                std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
+                s->what |= layer_state_t::eHasListenerCallbacksChanged;
+                s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
             }
-            s->what |= layer_state_t::eHasListenerCallbacksChanged;
-            s->hasListenerCallbacks = true;
         }
     }
+
     mListenerCallbacks.clear();
 
     cacheBuffers();
@@ -504,7 +662,7 @@
     sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands,
                             mDesiredPresentTime,
                             {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
-                            listenerCallbacks);
+                            hasListenerCallbacks, listenerCallbacks);
     mInputWindowCommands.clear();
     mStatus = NO_ERROR;
     return NO_ERROR;
@@ -545,16 +703,15 @@
     mEarlyWakeup = true;
 }
 
-layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
-    if (mComposerStates.count(sc) == 0) {
+layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) {
+    if (mComposerStates.count(handle) == 0) {
         // we don't have it, add an initialized layer_state to our list
         ComposerState s;
-        s.client = sc->getClient()->mClient;
-        s.state.surface = sc->getHandle();
-        mComposerStates[sc] = s;
+        s.state.surface = handle;
+        mComposerStates[handle] = s;
     }
 
-    return &(mComposerStates[sc].state);
+    return &(mComposerStates[handle].state);
 }
 
 void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
@@ -626,6 +783,7 @@
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
+        return *this;
     }
     s->what |= layer_state_t::eRelativeLayerChanged;
     s->what &= ~layer_state_t::eLayerChanged;
@@ -701,14 +859,15 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMetadata(
-        const sp<SurfaceControl>& sc, uint32_t key, std::vector<uint8_t> data) {
+        const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p) {
     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);
+
+    s->metadata.mMap[key] = {p.data(), p.data() + p.dataSize()};
 
     registerSurfaceControlForCallback(sc);
     return *this;
@@ -760,6 +919,18 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackgroundBlurRadius(
+        const sp<SurfaceControl>& sc, int backgroundBlurRadius) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eBackgroundBlurRadiusChanged;
+    s->backgroundBlurRadius = backgroundBlurRadius;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc,
                                                                  const sp<IBinder>& handle,
@@ -1034,6 +1205,22 @@
 }
 
 SurfaceComposerClient::Transaction&
+SurfaceComposerClient::Transaction::setFrameRateSelectionPriority(const sp<SurfaceControl>& sc,
+                                                                  int32_t priority) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eFrameRateSelectionPriority;
+    s->frameRateSelectionPriority = priority;
+
+    registerSurfaceControlForCallback(sc);
+    return *this;
+}
+
+SurfaceComposerClient::Transaction&
 SurfaceComposerClient::Transaction::addTransactionCompletedCallback(
         TransactionCompletedCallbackTakesContext callback, void* callbackContext) {
     auto listener = TransactionCompletedListener::getInstance();
@@ -1050,11 +1237,24 @@
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyProducerDisconnect(
+        const sp<SurfaceControl>& sc) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+
+    s->what |= layer_state_t::eProducerDisconnect;
+    return *this;
+}
+
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren(
         const sp<SurfaceControl>& sc) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
+        return *this;
     }
     s->what |= layer_state_t::eDetachChildren;
 
@@ -1091,19 +1291,6 @@
     return *this;
 }
 
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometryAppliesWithResize(
-        const sp<SurfaceControl>& sc) {
-    layer_state_t* s = getLayerState(sc);
-    if (!s) {
-        mStatus = BAD_INDEX;
-        return *this;
-    }
-    s->what |= layer_state_t::eGeometryAppliesWithResize;
-
-    registerSurfaceControlForCallback(sc);
-    return *this;
-}
-
 #ifndef NO_INPUT
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
         const sp<SurfaceControl>& sc,
@@ -1195,11 +1382,37 @@
             break;
     }
     setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]);
-    setPosition(sc, x, y);
+    float offsetX = xScale * source.left;
+    float offsetY = yScale * source.top;
+    setPosition(sc, x - offsetX, y - offsetY);
 
     return *this;
 }
 
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setShadowRadius(
+        const sp<SurfaceControl>& sc, float shadowRadius) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eShadowRadiusChanged;
+    s->shadowRadius = shadowRadius;
+    return *this;
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
+        const sp<SurfaceControl>& sc, float frameRate) {
+    layer_state_t* s = getLayerState(sc);
+    if (!s) {
+        mStatus = BAD_INDEX;
+        return *this;
+    }
+    s->what |= layer_state_t::eFrameRateChanged;
+    s->frameRate = frameRate;
+    return *this;
+}
+
 // ---------------------------------------------------------------------------
 
 DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -1241,9 +1454,9 @@
 }
 
 void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& token,
-        uint32_t orientation,
-        const Rect& layerStackRect,
-        const Rect& displayRect) {
+                                                              ui::Rotation orientation,
+                                                              const Rect& layerStackRect,
+                                                              const Rect& displayRect) {
     DisplayState& s(getDisplayState(token));
     s.orientation = orientation;
     s.viewport = layerStackRect;
@@ -1316,16 +1529,19 @@
 sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h,
                                                         PixelFormat format, uint32_t flags,
                                                         SurfaceControl* parent,
-                                                        LayerMetadata metadata) {
+                                                        LayerMetadata metadata,
+                                                        uint32_t* outTransformHint) {
     sp<SurfaceControl> s;
-    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata));
+    createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata),
+                         outTransformHint);
     return s;
 }
 
 sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& name, uint32_t w,
                                                                   uint32_t h, PixelFormat format,
                                                                   uint32_t flags, Surface* parent,
-                                                                  LayerMetadata metadata) {
+                                                                  LayerMetadata metadata,
+                                                                  uint32_t* outTransformHint) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
@@ -1334,11 +1550,15 @@
         sp<IGraphicBufferProducer> parentGbp = parent->getIGraphicBufferProducer();
         sp<IGraphicBufferProducer> gbp;
 
+        uint32_t transformHint = 0;
         err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp,
-                                               std::move(metadata), &handle, &gbp);
+                                               std::move(metadata), &handle, &gbp, &transformHint);
+        if (outTransformHint) {
+            *outTransformHint = transformHint;
+        }
         ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err));
         if (err == NO_ERROR) {
-            return new SurfaceControl(this, handle, gbp, true /* owned */);
+            return new SurfaceControl(this, handle, gbp, true /* owned */, transformHint);
         }
     }
     return nullptr;
@@ -1347,8 +1567,8 @@
 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) {
+                                                     SurfaceControl* parent, LayerMetadata metadata,
+                                                     uint32_t* outTransformHint) {
     sp<SurfaceControl> sur;
     status_t err = mStatus;
 
@@ -1361,16 +1581,34 @@
             parentHandle = parent->getHandle();
         }
 
+        uint32_t transformHint = 0;
         err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
-                                     &handle, &gbp);
+                                     &handle, &gbp, &transformHint);
+        if (outTransformHint) {
+            *outTransformHint = transformHint;
+        }
         ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
         if (err == NO_ERROR) {
-            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
+            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */, transformHint);
         }
     }
     return err;
 }
 
+sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFromSurface) {
+    if (mirrorFromSurface == nullptr) {
+        return nullptr;
+    }
+
+    sp<IBinder> handle;
+    sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle();
+    status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle);
+    if (err == NO_ERROR) {
+        return new SurfaceControl(this, handle, nullptr, true /* owned */);
+    }
+    return nullptr;
+}
+
 status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const {
     if (mStatus != NO_ERROR) {
         return mStatus;
@@ -1398,15 +1636,23 @@
     return sf->injectVSync(when);
 }
 
-status_t SurfaceComposerClient::getDisplayConfigs(
-        const sp<IBinder>& display, Vector<DisplayInfo>* configs)
-{
+status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display,
+                                                ui::DisplayState* state) {
+    return ComposerService::getComposerService()->getDisplayState(display, state);
+}
+
+status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) {
+    return ComposerService::getComposerService()->getDisplayInfo(display, info);
+}
+
+status_t SurfaceComposerClient::getDisplayConfigs(const sp<IBinder>& display,
+                                                  Vector<DisplayConfig>* configs) {
     return ComposerService::getComposerService()->getDisplayConfigs(display, configs);
 }
 
-status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display,
-        DisplayInfo* info) {
-    Vector<DisplayInfo> configs;
+status_t SurfaceComposerClient::getActiveDisplayConfig(const sp<IBinder>& display,
+                                                       DisplayConfig* config) {
+    Vector<DisplayConfig> configs;
     status_t result = getDisplayConfigs(display, &configs);
     if (result != NO_ERROR) {
         return result;
@@ -1418,7 +1664,7 @@
         return NAME_NOT_FOUND;
     }
 
-    *info = configs[static_cast<size_t>(activeId)];
+    *config = configs[static_cast<size_t>(activeId)];
     return NO_ERROR;
 }
 
@@ -1426,20 +1672,24 @@
     return ComposerService::getComposerService()->getActiveConfig(display);
 }
 
-status_t SurfaceComposerClient::setActiveConfig(const sp<IBinder>& display, int id) {
-    return ComposerService::getComposerService()->setActiveConfig(display, id);
+status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                             int32_t defaultConfig,
+                                                             float minRefreshRate,
+                                                             float maxRefreshRate) {
+    return ComposerService::getComposerService()->setDesiredDisplayConfigSpecs(displayToken,
+                                                                               defaultConfig,
+                                                                               minRefreshRate,
+                                                                               maxRefreshRate);
 }
 
-status_t SurfaceComposerClient::setAllowedDisplayConfigs(
-        const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) {
-    return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken,
-                                                                           allowedConfigs);
-}
-
-status_t SurfaceComposerClient::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                         std::vector<int32_t>* outAllowedConfigs) {
-    return ComposerService::getComposerService()->getAllowedDisplayConfigs(displayToken,
-                                                                           outAllowedConfigs);
+status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                             int32_t* outDefaultConfig,
+                                                             float* outMinRefreshRate,
+                                                             float* outMaxRefreshRate) {
+    return ComposerService::getComposerService()->getDesiredDisplayConfigSpecs(displayToken,
+                                                                               outDefaultConfig,
+                                                                               outMinRefreshRate,
+                                                                               outMaxRefreshRate);
 }
 
 status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
@@ -1461,6 +1711,26 @@
     return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
 }
 
+bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
+    bool supported = false;
+    ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
+    return supported;
+}
+
+void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+    ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
+}
+
+bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
+    bool supported = false;
+    ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
+    return supported;
+}
+
+void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
+    ComposerService::getComposerService()->setGameContentType(display, on);
+}
+
 void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
         int mode) {
     ComposerService::getComposerService()->setPowerMode(token, mode);
@@ -1552,30 +1822,36 @@
     return ComposerService::getComposerService()->notifyPowerHint(hintId);
 }
 
+status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
+                                                        const half4& spotColor, float lightPosY,
+                                                        float lightPosZ, float lightRadius) {
+    return ComposerService::getComposerService()->setGlobalShadowSettings(ambientColor, spotColor,
+                                                                          lightPosY, lightPosZ,
+                                                                          lightRadius);
+}
+
 // ----------------------------------------------------------------------------
 
-status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
-                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   uint32_t rotation, bool captureSecureLayers,
+                                   ui::Rotation rotation, bool captureSecureLayers,
                                    sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
-    status_t ret =
-            s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
-                             reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform,
-                             static_cast<ISurfaceComposer::Rotation>(rotation),
-                             captureSecureLayers);
+    status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace,
+                                    reqPixelFormat, sourceCrop, reqWidth, reqHeight,
+                                    useIdentityTransform, rotation, captureSecureLayers);
     if (ret != NO_ERROR) {
         return ret;
     }
     return ret;
 }
 
-status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
-                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   uint32_t rotation, sp<GraphicBuffer>* outBuffer) {
+                                   ui::Rotation rotation, sp<GraphicBuffer>* outBuffer) {
     bool ignored;
     return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight,
                    useIdentityTransform, rotation, false, outBuffer, ignored);
@@ -1588,9 +1864,8 @@
     return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer);
 }
 
-status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle,
-                                         const ui::Dataspace reqDataSpace,
-                                         const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
+                                         ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                          float frameScale, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == nullptr) return NO_INIT;
@@ -1600,8 +1875,8 @@
 }
 
 status_t ScreenshotClient::captureChildLayers(
-        const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
-        const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+        const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat,
+        const Rect& sourceCrop,
         const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles,
         float frameScale, sp<GraphicBuffer>* outBuffer) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
@@ -1611,5 +1886,5 @@
                              excludeHandles, frameScale, true /* childrenOnly */);
     return ret;
 }
-// ----------------------------------------------------------------------------
-}; // namespace android
+
+} // namespace android
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 55488da..6292388 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -45,36 +45,29 @@
 //  SurfaceControl
 // ============================================================================
 
-SurfaceControl::SurfaceControl(
-        const sp<SurfaceComposerClient>& client,
-        const sp<IBinder>& handle,
-        const sp<IGraphicBufferProducer>& gbp,
-        bool owned)
-    : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp), mOwned(owned)
-{
-}
+SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
+                               const sp<IGraphicBufferProducer>& gbp, bool owned,
+                               uint32_t transform)
+      : mClient(client),
+        mHandle(handle),
+        mGraphicBufferProducer(gbp),
+        mOwned(owned),
+        mTransformHint(transform) {}
 
 SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) {
     mClient = other->mClient;
     mHandle = other->mHandle;
     mGraphicBufferProducer = other->mGraphicBufferProducer;
     mOwned = false;
+    mTransformHint = other->mTransformHint;
 }
 
 SurfaceControl::~SurfaceControl()
 {
     // Avoid reparenting the server-side surface to null if we are not the owner of it,
     // meaning that we retrieved it from another process.
-    if (mClient != nullptr && mHandle != nullptr && mOwned) {
-        SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient());
-    }
-    release();
-}
-
-void SurfaceControl::destroy()
-{
-    if (isValid()) {
-        SurfaceComposerClient::Transaction().reparent(this, nullptr).apply();
+    if (mHandle != nullptr && mOwned) {
+        SurfaceComposerClient::doDropReferenceTransaction(mHandle);
     }
     release();
 }
@@ -179,15 +172,25 @@
     return mClient;
 }
 
+uint32_t SurfaceControl::getTransformHint() const {
+    Mutex::Autolock _l(mLock);
+    return mTransformHint;
+}
+
+void SurfaceControl::setTransformHint(uint32_t hint) {
+    Mutex::Autolock _l(mLock);
+    mTransformHint = hint;
+}
+
 void SurfaceControl::writeToParcel(Parcel* parcel)
 {
     parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient()));
     parcel->writeStrongBinder(mHandle);
     parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer));
+    parcel->writeUint32(mTransformHint);
 }
 
-sp<SurfaceControl> SurfaceControl::readFromParcel(Parcel* parcel)
-{
+sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) {
     sp<IBinder> client = parcel->readStrongBinder();
     sp<IBinder> handle = parcel->readStrongBinder();
     if (client == nullptr || handle == nullptr)
@@ -198,10 +201,12 @@
     sp<IBinder> gbp;
     parcel->readNullableStrongBinder(&gbp);
 
+    uint32_t transformHint = parcel->readUint32();
     // We aren't the original owner of the surface.
     return new SurfaceControl(new SurfaceComposerClient(
-                    interface_cast<ISurfaceComposerClient>(client)),
-            handle.get(), interface_cast<IGraphicBufferProducer>(gbp), false /* owned */);
+                                      interface_cast<ISurfaceComposerClient>(client)),
+                              handle.get(), interface_cast<IGraphicBufferProducer>(gbp),
+                              false /* owned */, transformHint);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING
new file mode 100644
index 0000000..1c43530
--- /dev/null
+++ b/libs/gui/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/native/libs/nativewindow"
+    }
+  ]
+}
diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp
index 5cb3593..3e20a37 100644
--- a/libs/gui/bufferqueue/1.0/Conversion.cpp
+++ b/libs/gui/bufferqueue/1.0/Conversion.cpp
@@ -109,20 +109,6 @@
 }
 
 /**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(
-        Return<void> const& t) {
-    return ::android::binder::Status::fromExceptionCode(
-            toStatusT(t),
-            t.description().c_str());
-}
-
-/**
  * \brief Wrap `native_handle_t*` in `hidl_handle`.
  *
  * \param[in] nh The source `native_handle_t*`.
@@ -1337,57 +1323,6 @@
     return unflatten(&(t->surfaceDamage), buffer, size);
 }
 
-/**
- * \brief Wrap `BGraphicBufferProducer::QueueBufferInput` in
- * `HGraphicBufferProducer::QueueBufferInput`.
- *
- * \param[out] t The wrapper of type
- * `HGraphicBufferProducer::QueueBufferInput`.
- * \param[out] nh The underlying native handle for `t->fence`.
- * \param[in] l The source `BGraphicBufferProducer::QueueBufferInput`.
- *
- * If the return value is `true` and `t->fence` contains a valid file
- * descriptor, \p nh will be a newly created native handle holding that file
- * descriptor. \p nh needs to be deleted with `native_handle_delete()`
- * afterwards.
- */
-bool wrapAs(
-        HGraphicBufferProducer::QueueBufferInput* t,
-        native_handle_t** nh,
-        BGraphicBufferProducer::QueueBufferInput const& l) {
-
-    size_t const baseSize = l.getFlattenedSize();
-    std::unique_ptr<uint8_t[]> baseBuffer(
-            new (std::nothrow) uint8_t[baseSize]);
-    if (!baseBuffer) {
-        return false;
-    }
-
-    size_t const baseNumFds = l.getFdCount();
-    std::unique_ptr<int[]> baseFds(
-            new (std::nothrow) int[baseNumFds]);
-    if (!baseFds) {
-        return false;
-    }
-
-    void* buffer = static_cast<void*>(baseBuffer.get());
-    size_t size = baseSize;
-    int* fds = baseFds.get();
-    size_t numFds = baseNumFds;
-    if (l.flatten(buffer, size, fds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    void const* constBuffer = static_cast<void const*>(baseBuffer.get());
-    size = baseSize;
-    int const* constFds = static_cast<int const*>(baseFds.get());
-    numFds = baseNumFds;
-    if (unflatten(t, nh, constBuffer, size, constFds, numFds) != NO_ERROR) {
-        return false;
-    }
-
-    return true;
-}
 
 /**
  * \brief Convert `HGraphicBufferProducer::QueueBufferInput` to
diff --git a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
index 2712f42..598c8de 100644
--- a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
 using ::android::hardware::Return;
 
 H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
       : CBase{base} {
+#else
+      : mBase(base) {
+#endif
 }
 
 void H2BProducerListener::onBufferReleased() {
diff --git a/libs/gui/bufferqueue/1.0/WProducerListener.cpp b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
index 78dc4e8..56b64b9 100644
--- a/libs/gui/bufferqueue/1.0/WProducerListener.cpp
+++ b/libs/gui/bufferqueue/1.0/WProducerListener.cpp
@@ -46,5 +46,7 @@
 bool LWProducerListener::needsReleaseNotify() {
     return static_cast<bool>(mBase->needsReleaseNotify());
 }
+void LWProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
 
 }  // namespace android
diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
index e891ec5..c76d771 100644
--- a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
+++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp
@@ -249,6 +249,24 @@
     return {};
 }
 
+struct Obituary : public hardware::hidl_death_recipient {
+    wp<B2HGraphicBufferProducer> producer;
+    sp<HProducerListener> listener;
+    HConnectionType apiType;
+    Obituary(const wp<B2HGraphicBufferProducer>& p,
+             const sp<HProducerListener>& l,
+             HConnectionType t)
+        : producer(p), listener(l), apiType(t) {}
+
+    void serviceDied(uint64_t /* cookie */,
+            const wp<::android::hidl::base::V1_0::IBase>& /* who */) override {
+        sp<B2HGraphicBufferProducer> dr = producer.promote();
+        if (dr != nullptr) {
+            (void)dr->disconnect(apiType);
+        }
+    }
+};
+
 Return<void> B2HGraphicBufferProducer::connect(
         sp<HProducerListener> const& hListener,
         HConnectionType hConnectionType,
@@ -270,6 +288,12 @@
                                &bOutput),
                 &hStatus) &&
             b2h(bOutput, &hOutput);
+#ifdef NO_BINDER
+    if (converted && hListener != nullptr) {
+        mObituary = new Obituary(this, hListener, hConnectionType);
+        hListener->linkToDeath(mObituary, 0);
+    }
+#endif // NO_BINDER
     _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, hOutput);
     return {};
 }
@@ -282,6 +306,12 @@
     }
     HStatus hStatus{};
     bool converted = b2h(mBase->disconnect(bConnectionType), &hStatus);
+#ifdef NO_BINDER
+    if (mObituary != nullptr) {
+        mObituary->listener->unlinkToDeath(mObituary);
+        mObituary.clear();
+    }
+#endif // NO_BINDER
     return {converted ? hStatus : HStatus::UNKNOWN_ERROR};
 }
 
diff --git a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
index b81a357..b2bd117 100644
--- a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
+++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp
@@ -32,7 +32,11 @@
 using ::android::hardware::Return;
 
 H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base)
+#ifndef NO_BINDER
       : CBase{base} {
+#else
+      : mBase(base) {
+#endif
 }
 
 void H2BProducerListener::onBufferReleased() {
@@ -48,6 +52,9 @@
     return static_cast<bool>(mBase);
 }
 
+void H2BProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*slots*/) {
+}
+
 }  // namespace utils
 }  // namespace V2_0
 }  // namespace bufferqueue
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
new file mode 100644
index 0000000..1b22df2
--- /dev/null
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -0,0 +1,130 @@
+/*
+ * 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_GUI_BLAST_BUFFER_QUEUE_H
+#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferItem.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+
+#include <system/window.h>
+#include <thread>
+
+namespace android {
+
+class BufferItemConsumer;
+
+class BLASTBufferItemConsumer : public BufferItemConsumer {
+public:
+    BLASTBufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
+                            int bufferCount, bool controlledByApp)
+          : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp),
+            mCurrentlyConnected(false),
+            mPreviouslyConnected(false) {}
+
+    void onDisconnect() override;
+    void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
+                                  FrameEventHistoryDelta* outDelta) override
+            REQUIRES(mFrameEventHistoryMutex);
+    void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime,
+                               const sp<Fence>& gpuCompositionDoneFence,
+                               const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
+                               CompositorTiming compositorTiming, nsecs_t latchTime,
+                               nsecs_t dequeueReadyTime) REQUIRES(mFrameEventHistoryMutex);
+    void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect);
+
+private:
+    uint64_t mCurrentFrameNumber = 0;
+
+    Mutex mFrameEventHistoryMutex;
+    ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mFrameEventHistoryMutex);
+    std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mFrameEventHistoryMutex);
+    bool mCurrentlyConnected GUARDED_BY(mFrameEventHistoryMutex);
+    bool mPreviouslyConnected GUARDED_BY(mFrameEventHistoryMutex);
+};
+
+class BLASTBufferQueue
+    : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
+{
+public:
+    BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height);
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+        return mProducer;
+    }
+
+    void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ }
+    void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);}
+    void onFrameAvailable(const BufferItem& item) override;
+
+    void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+            const std::vector<SurfaceControlStats>& stats);
+    void setNextTransaction(SurfaceComposerClient::Transaction *t);
+
+    void update(const sp<SurfaceControl>& surface, int width, int height);
+
+    virtual ~BLASTBufferQueue() = default;
+
+private:
+    friend class BLASTBufferQueueHelper;
+
+    // can't be copied
+    BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs);
+    BLASTBufferQueue(const BLASTBufferQueue& rhs);
+
+    void processNextBufferLocked() REQUIRES(mMutex);
+    Rect computeCrop(const BufferItem& item);
+
+    sp<SurfaceControl> mSurfaceControl;
+
+    std::mutex mMutex;
+    std::condition_variable mCallbackCV;
+
+    // BufferQueue internally allows 1 more than
+    // the max to be acquired
+    static const int MAX_ACQUIRED_BUFFERS = 1;
+
+    int32_t mNumFrameAvailable GUARDED_BY(mMutex);
+    int32_t mNumAcquired GUARDED_BY(mMutex);
+
+    struct PendingReleaseItem {
+        BufferItem item;
+        sp<Fence> releaseFence;
+    };
+
+    std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+    PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
+
+    int mWidth GUARDED_BY(mMutex);
+    int mHeight GUARDED_BY(mMutex);
+
+    uint32_t mTransformHint GUARDED_BY(mMutex);
+
+    sp<IGraphicBufferConsumer> mConsumer;
+    sp<IGraphicBufferProducer> mProducer;
+    sp<BLASTBufferItemConsumer> mBufferItemConsumer;
+
+    SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+};
+
+} // namespace android
+
+#endif  // ANDROID_GUI_SURFACE_H
diff --git a/libs/gui/include/gui/BufferHubConsumer.h b/libs/gui/include/gui/BufferHubConsumer.h
index d380770..d756203 100644
--- a/libs/gui/include/gui/BufferHubConsumer.h
+++ b/libs/gui/include/gui/BufferHubConsumer.h
@@ -93,6 +93,12 @@
     // See |IGraphicBufferConsumer::discardFreeBuffers|
     status_t discardFreeBuffers() override;
 
+    // See |IGraphicBufferConsumer::setFrameRate|
+    status_t setFrameRate(float frameRate) override;
+
+    // See |IGraphicBufferConsumer::getFrameRate|
+    status_t getFrameRate(float* frameRate) const override;
+
     // See |IGraphicBufferConsumer::dumpState|
     status_t dumpState(const String8& prefix, String8* outResult) const override;
 
diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h
index da95274..91f80d2 100644
--- a/libs/gui/include/gui/BufferQueue.h
+++ b/libs/gui/include/gui/BufferQueue.h
@@ -63,6 +63,9 @@
         void onFrameReplaced(const BufferItem& item) override;
         void onBuffersReleased() override;
         void onSidebandStreamChanged() override;
+        void onFrameDequeued(const uint64_t bufferId) override;
+        void onFrameCancelled(const uint64_t bufferId) override;
+        void onFrameDetached(const uint64_t bufferID) override;
         void addAndGetFrameTimestamps(
                 const NewFrameEventsEntry* newTimestamps,
                 FrameEventHistoryDelta* outDelta) override;
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index 7db69ec..e9f0449 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -149,6 +149,12 @@
     // See IGraphicBufferConsumer::discardFreeBuffers
     virtual status_t discardFreeBuffers() override;
 
+    // See IGraphicBufferConsumer::setFrameRate.
+    virtual status_t setFrameRate(float frameRate) override;
+
+    // See IGraphicBufferConsumer::getFrameRate.
+    virtual status_t getFrameRate(float* frameRate) const override;
+
     // dump our state in a String
     status_t dumpState(const String8& prefix, String8* outResult) const override;
 
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 17617bc..05c2074 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -34,12 +34,6 @@
 #include <mutex>
 #include <condition_variable>
 
-#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-
 #define ATRACE_BUFFER_INDEX(index)                                                         \
     do {                                                                                   \
         if (ATRACE_ENABLED()) {                                                            \
@@ -352,6 +346,17 @@
 
     const uint64_t mUniqueId;
 
+    // When buffer size is driven by the consumer and mTransformHint specifies
+    // a 90 or 270 degree rotation, this indicates whether the width and height
+    // used by dequeueBuffer will be additionally swapped.
+    bool mAutoPrerotation;
+
+    // mTransformHintInUse is to cache the mTransformHint used by the producer.
+    uint32_t mTransformHintInUse;
+
+    // The frame rate the app intends to run at.
+    float mFrameRate;
+
 }; // class BufferQueueCore
 
 } // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index d2a47a6..cbace5b 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -22,10 +22,15 @@
 
 namespace android {
 
+class IBinder;
 struct BufferSlot;
 
+#ifndef NO_BINDER
 class BufferQueueProducer : public BnGraphicBufferProducer,
                             private IBinder::DeathRecipient {
+#else
+class BufferQueueProducer : public BnGraphicBufferProducer {
+#endif
 public:
     friend class BufferQueue; // Needed to access binderDied
 
@@ -190,6 +195,12 @@
     // See IGraphicBufferProducer::getConsumerUsage
     virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
 
+    // See IGraphicBufferProducer::setAutoPrerotation
+    virtual status_t setAutoPrerotation(bool autoPrerotation);
+
+    // See IGraphicBufferProducer::setFrameRate
+    virtual status_t setFrameRate(float frameRate) override;
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index 366ced3..cfed9aa 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -45,6 +45,9 @@
         // See IConsumerListener::onFrame{Available,Replaced}
         virtual void onFrameAvailable(const BufferItem& item) = 0;
         virtual void onFrameReplaced(const BufferItem& /* item */) {}
+        virtual void onFrameDequeued(const uint64_t){};
+        virtual void onFrameCancelled(const uint64_t){};
+        virtual void onFrameDetached(const uint64_t){};
     };
 
     ~ConsumerBase() override;
@@ -108,6 +111,12 @@
     // See IGraphicBufferConsumer::discardFreeBuffers
     status_t discardFreeBuffers();
 
+    // See IGraphicBufferConsumer::setFrameRate
+    status_t setFrameRate(float frameRate);
+
+    // See IGraphicBufferConsumer::getFrameRate
+    status_t getFrameRate(float* frameRate);
+
 private:
     ConsumerBase(const ConsumerBase&);
     void operator=(const ConsumerBase&);
@@ -141,6 +150,9 @@
     // classes if they want the notification.
     virtual void onFrameAvailable(const BufferItem& item) override;
     virtual void onFrameReplaced(const BufferItem& item) override;
+    virtual void onFrameDequeued(const uint64_t bufferId) override;
+    virtual void onFrameCancelled(const uint64_t bufferId) override;
+    virtual void onFrameDetached(const uint64_t bufferId) override;
     virtual void onBuffersReleased() override;
     virtual void onSidebandStreamChanged() override;
 
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
new file mode 100644
index 0000000..fcdf6bf
--- /dev/null
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+namespace android {
+
+class DisplayEventDispatcher : public LooperCallback {
+public:
+    explicit DisplayEventDispatcher(
+            const sp<Looper>& looper,
+            ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+            ISurfaceComposer::ConfigChanged configChanged =
+                    ISurfaceComposer::eConfigChangedSuppress);
+
+    status_t initialize();
+    void dispose();
+    status_t scheduleVsync();
+    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+    int getFd() const;
+    virtual int handleEvent(int receiveFd, int events, void* data);
+
+protected:
+    virtual ~DisplayEventDispatcher() = default;
+
+private:
+    sp<Looper> mLooper;
+    DisplayEventReceiver mReceiver;
+    bool mWaitingForVsync;
+    ISurfaceComposer::ConfigChanged mConfigChangeFlag;
+
+    virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+    virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                 bool connected) = 0;
+    virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+                                       int32_t configId, nsecs_t vsyncPeriod) = 0;
+
+    bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
+                              uint32_t* outCount);
+};
+} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index a558cf9..109e28b 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -73,6 +73,7 @@
 
         struct Config {
             int32_t configId;
+            nsecs_t vsyncPeriod;
         };
 
         Header header;
@@ -144,6 +145,11 @@
      */
     status_t requestNextVsync();
 
+    /*
+     * toggleConfigEvents() toggles delivery of config change events.
+     */
+    status_t toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag);
+
 private:
     sp<IDisplayEventConnection> mEventConnection;
     std::unique_ptr<gui::BitTube> mDataChannel;
diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h
index 4670edd..0750080 100644
--- a/libs/gui/include/gui/FrameTimestamps.h
+++ b/libs/gui/include/gui/FrameTimestamps.h
@@ -175,7 +175,6 @@
     std::shared_ptr<FenceTime> acquireFence{FenceTime::NO_FENCE};
 };
 
-
 // Used by the consumer to keep track of which fields it already sent to
 // the producer.
 class FrameEventDirtyFields {
@@ -209,6 +208,7 @@
     ~ConsumerFrameEventHistory() override;
 
     void onDisconnect();
+    void setProducerWantsEvents();
 
     void initializeCompositorTiming(const CompositorTiming& compositorTiming);
 
diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h
index c082882..0ab2399 100644
--- a/libs/gui/include/gui/IConsumerListener.h
+++ b/libs/gui/include/gui/IConsumerListener.h
@@ -43,6 +43,17 @@
     // onDisconnect is called when a producer disconnects from the BufferQueue.
     virtual void onDisconnect() {} /* Asynchronous */
 
+    // onFrameDequeued is called when a call to the BufferQueueProducer::dequeueBuffer successfully
+    // returns a slot from the BufferQueue.
+    virtual void onFrameDequeued(const uint64_t) {}
+
+    // onFrameCancelled is called when the client calls cancelBuffer, thereby releasing the slot
+    // back to the BufferQueue.
+    virtual void onFrameCancelled(const uint64_t) {}
+
+    // onFrameDetached is called after a successful detachBuffer() call while in asynchronous mode.
+    virtual void onFrameDetached(const uint64_t) {}
+
     // onFrameAvailable is called from queueBuffer each time an additional frame becomes available
     // for consumption. This means that frames that are queued while in asynchronous mode only
     // trigger the callback if no previous frames are pending. Frames queued while in synchronous
@@ -81,6 +92,7 @@
                                           FrameEventHistoryDelta* /*outDelta*/) {}
 };
 
+#ifndef NO_BINDER
 class IConsumerListener : public ConsumerListener, public IInterface {
 public:
     DECLARE_META_INTERFACE(ConsumerListener)
@@ -94,4 +106,11 @@
                         uint32_t flags = 0) override;
 };
 
+#else
+class IConsumerListener : public ConsumerListener {
+};
+class BnConsumerListener : public IConsumerListener {
+};
+#endif
+
 } // namespace android
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
index d783f74..8b35ef6 100644
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ b/libs/gui/include/gui/IDisplayEventConnection.h
@@ -18,7 +18,7 @@
 
 #include <binder/IInterface.h>
 #include <binder/SafeInterface.h>
-
+#include <gui/ISurfaceComposer.h>
 #include <utils/Errors.h>
 
 #include <cstdint>
@@ -51,6 +51,13 @@
      * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
      */
     virtual void requestNextVsync() = 0; // Asynchronous
+
+    /*
+     * togglesConfigEvents() configures whether or not display config changes
+     * should be propagated.
+     */
+    virtual void toggleConfigEvents(
+            ISurfaceComposer::ConfigChanged configChangeFlag) = 0; // Asynchronous
 };
 
 class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 9fb7580..56fe949 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -35,10 +35,14 @@
 class GraphicBuffer;
 class IConsumerListener;
 class NativeHandle;
-
+#ifndef NO_BINDER
 class IGraphicBufferConsumer : public IInterface {
 public:
     DECLARE_META_INTERFACE(GraphicBufferConsumer)
+#else
+class IGraphicBufferConsumer : public RefBase {
+public:
+#endif
 
     enum {
         // Returned by releaseBuffer, after which the consumer must free any references to the
@@ -271,6 +275,16 @@
     // call to free up any of its locally cached buffers.
     virtual status_t discardFreeBuffers() = 0;
 
+    // Set the frame rate the producer will run at.
+    //
+    // Return of a value other than NO_ERROR means an unknown error has occurred.
+    virtual status_t setFrameRate(float frameRate) = 0;
+
+    // Get the frame rate the producer will run at.
+    //
+    // Return of a value other than NO_ERROR means an unknown error has occurred.
+    virtual status_t getFrameRate(float* frameRate) const = 0;
+
     // dump state into a string
     virtual status_t dumpState(const String8& prefix, String8* outResult) const = 0;
 
@@ -282,6 +296,7 @@
     }
 };
 
+#ifndef NO_BINDER
 class BnGraphicBufferConsumer : public SafeBnInterface<IGraphicBufferConsumer> {
 public:
     BnGraphicBufferConsumer()
@@ -290,5 +305,9 @@
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                         uint32_t flags = 0) override;
 };
+#else
+class BnGraphicBufferConsumer : public IGraphicBufferConsumer {
+};
+#endif
 
 } // namespace android
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 3dde8c8..87989da 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -45,6 +45,13 @@
 class NativeHandle;
 class Surface;
 
+using HGraphicBufferProducerV1_0 =
+        ::android::hardware::graphics::bufferqueue::V1_0::
+        IGraphicBufferProducer;
+using HGraphicBufferProducerV2_0 =
+        ::android::hardware::graphics::bufferqueue::V2_0::
+        IGraphicBufferProducer;
+
 /*
  * This class defines the Binder IPC interface for the producer side of
  * a queue of graphics buffers.  It's used to send graphics data from one
@@ -59,20 +66,15 @@
  *
  * This class was previously called ISurfaceTexture.
  */
-class IGraphicBufferProducer : public IInterface
-{
-public:
-    using HGraphicBufferProducerV1_0 =
-            ::android::hardware::graphics::bufferqueue::V1_0::
-            IGraphicBufferProducer;
-    using HGraphicBufferProducerV2_0 =
-            ::android::hardware::graphics::bufferqueue::V2_0::
-            IGraphicBufferProducer;
-
+#ifndef NO_BINDER
+class IGraphicBufferProducer : public IInterface {
     DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer,
                                   HGraphicBufferProducerV1_0,
                                   HGraphicBufferProducerV2_0)
-
+#else
+class IGraphicBufferProducer : public RefBase {
+#endif
+public:
     enum {
         // A flag returned by dequeueBuffer when the client needs to call
         // requestBuffer immediately thereafter.
@@ -286,36 +288,6 @@
     virtual status_t attachBuffer(int* outSlot,
             const sp<GraphicBuffer>& buffer) = 0;
 
-    // queueBuffer indicates that the client has finished filling in the
-    // contents of the buffer associated with slot and transfers ownership of
-    // that slot back to the server.
-    //
-    // It is not valid to call queueBuffer on a slot that is not owned
-    // by the client or one for which a buffer associated via requestBuffer
-    // (an attempt to do so will fail with a return value of BAD_VALUE).
-    //
-    // In addition, the input must be described by the client (as documented
-    // below). Any other properties (zero point, etc)
-    // are client-dependent, and should be documented by the client.
-    //
-    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
-    //
-    // Upon success, the output will be filled with meaningful values
-    // (refer to the documentation below).
-    //
-    // Return of a value other than NO_ERROR means an error has occurred:
-    // * NO_INIT - the buffer queue has been abandoned or the producer is not
-    //             connected.
-    // * BAD_VALUE - one of the below conditions occurred:
-    //              * fence was NULL
-    //              * scaling mode was unknown
-    //              * both in async mode and buffer count was less than the
-    //                max numbers of buffers that can be allocated at once
-    //              * slot index was out of range (see above).
-    //              * the slot was not in the dequeued state
-    //              * the slot was enqueued without requesting a buffer
-    //              * crop rect is out of bounds of the buffer dimensions
-
     struct QueueBufferInput : public Flattenable<QueueBufferInput> {
         friend class Flattenable<QueueBufferInput>;
         explicit inline QueueBufferInput(const Parcel& parcel);
@@ -412,8 +384,38 @@
         uint64_t nextFrameNumber{0};
         FrameEventHistoryDelta frameTimestamps;
         bool bufferReplaced{false};
+        int maxBufferCount{0};
     };
 
+    // queueBuffer indicates that the client has finished filling in the
+    // contents of the buffer associated with slot and transfers ownership of
+    // that slot back to the server.
+    //
+    // It is not valid to call queueBuffer on a slot that is not owned
+    // by the client or one for which a buffer associated via requestBuffer
+    // (an attempt to do so will fail with a return value of BAD_VALUE).
+    //
+    // In addition, the input must be described by the client (as documented
+    // below). Any other properties (zero point, etc)
+    // are client-dependent, and should be documented by the client.
+    //
+    // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+    //
+    // Upon success, the output will be filled with meaningful values
+    // (refer to the documentation below).
+    //
+    // Return of a value other than NO_ERROR means an error has occurred:
+    // * NO_INIT - the buffer queue has been abandoned or the producer is not
+    //             connected.
+    // * BAD_VALUE - one of the below conditions occurred:
+    //              * fence was NULL
+    //              * scaling mode was unknown
+    //              * both in async mode and buffer count was less than the
+    //                max numbers of buffers that can be allocated at once
+    //              * slot index was out of range (see above).
+    //              * the slot was not in the dequeued state
+    //              * the slot was enqueued without requesting a buffer
+    //              * crop rect is out of bounds of the buffer dimensions
     virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
             QueueBufferOutput* output) = 0;
 
@@ -629,6 +631,18 @@
     // NATIVE_WINDOW_CONSUMER_USAGE_BITS attribute.
     virtual status_t getConsumerUsage(uint64_t* outUsage) const = 0;
 
+    // Enable/disable the auto prerotation at buffer allocation when the buffer
+    // size is driven by the consumer.
+    //
+    // When buffer size is driven by the consumer and the transform hint
+    // specifies a 90 or 270 degree rotation, if auto prerotation is enabled,
+    // the width and height used for dequeueBuffer will be additionally swapped.
+    virtual status_t setAutoPrerotation(bool autoPrerotation);
+
+    // Sets the apps intended frame rate.
+    virtual status_t setFrameRate(float frameRate);
+
+#ifndef NO_BINDER
     // Static method exports any IGraphicBufferProducer object to a parcel. It
     // handles null producer as well.
     static status_t exportToParcel(const sp<IGraphicBufferProducer>& producer,
@@ -646,10 +660,11 @@
     // it writes a strong binder object; for BufferHub, it writes a
     // ProducerQueueParcelable object.
     virtual status_t exportToParcel(Parcel* parcel);
+#endif
 };
 
 // ----------------------------------------------------------------------------
-
+#ifndef NO_BINDER
 class BnGraphicBufferProducer : public BnInterface<IGraphicBufferProducer>
 {
 public:
@@ -658,6 +673,10 @@
                                     Parcel* reply,
                                     uint32_t flags = 0);
 };
+#else
+class BnGraphicBufferProducer : public IGraphicBufferProducer {
+};
+#endif
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 32a3690..0b1f4b5 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -51,6 +51,7 @@
     virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous
 };
 
+#ifndef NO_BINDER
 class IProducerListener : public ProducerListener, public IInterface
 {
 public:
@@ -73,6 +74,12 @@
     virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
 };
 
+#else
+class IProducerListener : public ProducerListener {
+};
+class BnProducerListener : public IProducerListener {
+};
+#endif
 class DummyProducerListener : public BnProducerListener
 {
 public:
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index c84910b..e860f61 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_ISURFACE_COMPOSER_H
-#define ANDROID_GUI_ISURFACE_COMPOSER_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -25,12 +24,16 @@
 
 #include <gui/ITransactionCompletedListener.h>
 
+#include <math/vec4.h>
+
 #include <ui/ConfigStoreTypes.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <ui/GraphicTypes.h>
+#include <ui/PhysicalDisplayId.h>
 #include <ui/PixelFormat.h>
+#include <ui/Rotation.h>
 
 #include <utils/Errors.h>
 #include <utils/RefBase.h>
@@ -42,13 +45,13 @@
 #include <vector>
 
 namespace android {
-// ----------------------------------------------------------------------------
 
 struct client_cache_t;
 struct ComposerState;
-struct DisplayState;
+struct DisplayConfig;
 struct DisplayInfo;
 struct DisplayStatInfo;
+struct DisplayState;
 struct InputWindowCommands;
 class LayerDebugInfo;
 class HdrCapabilities;
@@ -59,6 +62,12 @@
 class Rect;
 enum class FrameEvent;
 
+namespace ui {
+
+struct DisplayState;
+
+} // namespace ui
+
 /*
  * This class defines the Binder IPC interface for accessing various
  * SurfaceFlinger features.
@@ -78,13 +87,6 @@
         eEarlyWakeup = 0x04
     };
 
-    enum Rotation {
-        eRotateNone = 0,
-        eRotate90   = 1,
-        eRotate180  = 2,
-        eRotate270  = 3
-    };
-
     enum VsyncSource {
         eVsyncSourceApp = 0,
         eVsyncSourceSurfaceFlinger = 1
@@ -140,7 +142,7 @@
                                      const sp<IBinder>& applyToken,
                                      const InputWindowCommands& inputWindowCommands,
                                      int64_t desiredPresentTime,
-                                     const client_cache_t& uncacheBuffer,
+                                     const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
                                      const std::vector<ListenerCallbacks>& listenerCallbacks) = 0;
 
     /* signal that we're done booting.
@@ -164,10 +166,6 @@
      */
     virtual void setPowerMode(const sp<IBinder>& display, int mode) = 0;
 
-    /* returns information for each configuration of the given display
-     * intended to be used to get information about built-in displays */
-    virtual status_t getDisplayConfigs(const sp<IBinder>& display,
-            Vector<DisplayInfo>* configs) = 0;
 
     /* returns display statistics for a given display
      * intended to be used by the media framework to properly schedule
@@ -175,13 +173,26 @@
     virtual status_t getDisplayStats(const sp<IBinder>& display,
             DisplayStatInfo* stats) = 0;
 
-    /* indicates which of the configurations returned by getDisplayInfo is
-     * currently active */
-    virtual int getActiveConfig(const sp<IBinder>& display) = 0;
+    /**
+     * Get transactional state of given display.
+     */
+    virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*) = 0;
 
-    /* specifies which configuration (of those returned by getDisplayInfo)
-     * should be used */
-    virtual status_t setActiveConfig(const sp<IBinder>& display, int id) = 0;
+    /**
+     * Get immutable information about given physical display.
+     */
+    virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0;
+
+    /**
+     * Get configurations supported by given physical display.
+     */
+    virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*) = 0;
+
+    /**
+     * Get the index into configurations returned by getDisplayConfigs,
+     * corresponding to the active configuration.
+     */
+    virtual int getActiveConfig(const sp<IBinder>& display) = 0;
 
     virtual status_t getDisplayColorModes(const sp<IBinder>& display,
             Vector<ui::ColorMode>* outColorModes) = 0;
@@ -192,6 +203,37 @@
             ui::ColorMode colorMode) = 0;
 
     /**
+     * Returns true if the connected display reports support for HDMI 2.1 Auto
+     * Low Latency Mode.
+     * For more information, see the HDMI 2.1 specification.
+     */
+    virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+                                                  bool* outSupport) const = 0;
+
+    /**
+     * Switches Auto Low Latency Mode on/off on the connected display, if it is
+     * available. This should only be called if #getAutoLowLatencyMode returns
+     * true.
+     * For more information, see the HDMI 2.1 specification.
+     */
+    virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
+
+    /**
+     * Returns true if the connected display reports support for Game Content Type.
+     * For more information, see the HDMI 1.4 specification.
+     */
+    virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
+                                               bool* outSupport) const = 0;
+
+    /**
+     * This will start sending infoframes to the connected display with
+     * ContentType=Game (if on=true). This will switch the disply to Game mode.
+     * This should only be called if #getGameContentTypeSupport returns true.
+     * For more information, see the HDMI 1.4 specification.
+     */
+    virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
+
+    /**
      * Capture the specified screen. This requires READ_FRAME_BUFFER
      * permission.  This function will fail if there is a secure window on
      * screen.
@@ -215,10 +257,10 @@
      * it) around its center.
      */
     virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
-                                   const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+                                   bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
+                                   ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                                    uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                                   Rotation rotation = eRotateNone,
+                                   ui::Rotation rotation = ui::ROTATION_0,
                                    bool captureSecureLayers = false) = 0;
     /**
      * Capture the specified screen. This requires READ_FRAME_BUFFER
@@ -242,8 +284,9 @@
      * it) around its center.
      */
     virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
-                                   Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
-                                   bool useIdentityTransform, Rotation rotation = eRotateNone) {
+                                   const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
+                                   bool useIdentityTransform,
+                                   ui::Rotation rotation = ui::ROTATION_0) {
         bool outIgnored;
         return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB,
                              ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight,
@@ -267,8 +310,7 @@
      */
     virtual status_t captureLayers(
             const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer,
-            const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat,
-            const Rect& sourceCrop,
+            ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
             const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles,
             float frameScale = 1.0, bool childrenOnly = false) = 0;
 
@@ -383,20 +425,18 @@
     virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
 
     /*
-     * Sets the allowed display configurations to be used.
-     * The allowedConfigs in a vector of indexes corresponding to the configurations
-     * returned from getDisplayConfigs().
+     * Sets the refresh rate boundaries for display configuration.
+     * For all other parameters, default configuration is used. The index for the default is
+     * corresponding to the configs returned from getDisplayConfigs().
      */
-    virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              const std::vector<int32_t>& allowedConfigs) = 0;
+    virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                  int32_t defaultConfig, float minRefreshRate,
+                                                  float maxRefreshRate) = 0;
 
-    /*
-     * Returns the allowed display configurations currently set.
-     * The allowedConfigs in a vector of indexes corresponding to the configurations
-     * returned from getDisplayConfigs().
-     */
-    virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                              std::vector<int32_t>* outAllowedConfigs) = 0;
+    virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                  int32_t* outDefaultConfig,
+                                                  float* outMinRefreshRate,
+                                                  float* outMaxRefreshRate) = 0;
     /*
      * Gets whether brightness operations are supported on a display.
      *
@@ -438,6 +478,28 @@
      * Returns NO_ERROR upon success.
      */
     virtual status_t notifyPowerHint(int32_t hintId) = 0;
+
+    /*
+     * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+     * material design guidelines.
+     *
+     * ambientColor
+     *      Color to the ambient shadow. The alpha is premultiplied.
+     *
+     * spotColor
+     *      Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+     *      depends on the light position.
+     *
+     * lightPosY/lightPosZ
+     *      Position of the light used to cast the spot shadow. The X value is always the display
+     *      width / 2.
+     *
+     * lightRadius
+     *      Radius of the light casting the shadow.
+     */
+    virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+                                             float lightPosY, float lightPosZ,
+                                             float lightRadius) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -449,7 +511,7 @@
         // Java by ActivityManagerService.
         BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
         CREATE_CONNECTION,
-        CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED, // unused, fails permissions check
+        GET_DISPLAY_INFO,
         CREATE_DISPLAY_EVENT_CONNECTION,
         CREATE_DISPLAY,
         DESTROY_DISPLAY,
@@ -459,8 +521,7 @@
         GET_SUPPORTED_FRAME_TIMESTAMPS,
         GET_DISPLAY_CONFIGS,
         GET_ACTIVE_CONFIG,
-        SET_ACTIVE_CONFIG,
-        CONNECT_DISPLAY_UNUSED, // unused, fails permissions check
+        GET_DISPLAY_STATE,
         CAPTURE_SCREEN,
         CAPTURE_LAYERS,
         CLEAR_ANIMATION_FRAME_STATS,
@@ -485,12 +546,17 @@
         GET_PHYSICAL_DISPLAY_IDS,
         ADD_REGION_SAMPLING_LISTENER,
         REMOVE_REGION_SAMPLING_LISTENER,
-        SET_ALLOWED_DISPLAY_CONFIGS,
-        GET_ALLOWED_DISPLAY_CONFIGS,
+        SET_DESIRED_DISPLAY_CONFIG_SPECS,
+        GET_DESIRED_DISPLAY_CONFIG_SPECS,
         GET_DISPLAY_BRIGHTNESS_SUPPORT,
         SET_DISPLAY_BRIGHTNESS,
         CAPTURE_SCREEN_BY_ID,
         NOTIFY_POWER_HINT,
+        SET_GLOBAL_SHADOW_SETTINGS,
+        GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+        SET_AUTO_LOW_LATENCY_MODE,
+        GET_GAME_CONTENT_TYPE_SUPPORT,
+        SET_GAME_CONTENT_TYPE,
         // Always append new enum to the end.
     };
 
@@ -498,8 +564,4 @@
             Parcel* reply, uint32_t flags = 0);
 };
 
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_ISURFACE_COMPOSER_H
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h
index 32ac9e8..6366529 100644
--- a/libs/gui/include/gui/ISurfaceComposerClient.h
+++ b/libs/gui/include/gui/ISurfaceComposerClient.h
@@ -44,7 +44,7 @@
         eCursorWindow = 0x00002000,
 
         eFXSurfaceBufferQueue = 0x00000000,
-        eFXSurfaceColor = 0x00020000,
+        eFXSurfaceEffect = 0x00020000,
         eFXSurfaceBufferState = 0x00040000,
         eFXSurfaceContainer = 0x00080000,
         eFXSurfaceMask = 0x000F0000,
@@ -56,7 +56,7 @@
     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) = 0;
+                                   sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -65,7 +65,8 @@
                                              PixelFormat format, uint32_t flags,
                                              const sp<IGraphicBufferProducer>& parent,
                                              LayerMetadata metadata, sp<IBinder>* handle,
-                                             sp<IGraphicBufferProducer>* gbp) = 0;
+                                             sp<IGraphicBufferProducer>* gbp,
+                                             uint32_t* outTransformHint) = 0;
 
     /*
      * Requires ACCESS_SURFACE_FLINGER permission
@@ -76,6 +77,8 @@
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0;
+
+    virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0;
 };
 
 class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> {
diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h
index 774ad46..c58634b 100644
--- a/libs/gui/include/gui/ITransactionCompletedListener.h
+++ b/libs/gui/include/gui/ITransactionCompletedListener.h
@@ -21,6 +21,7 @@
 #include <binder/Parcelable.h>
 #include <binder/SafeInterface.h>
 
+#include <gui/FrameTimestamps.h>
 #include <ui/Fence.h>
 #include <utils/Timers.h>
 
@@ -31,21 +32,50 @@
 namespace android {
 
 class ITransactionCompletedListener;
+class ListenerCallbacks;
 
 using CallbackId = int64_t;
 
+class FrameEventHistoryStats : public Parcelable {
+public:
+    status_t writeToParcel(Parcel* output) const override;
+    status_t readFromParcel(const Parcel* input) override;
+
+    FrameEventHistoryStats() = default;
+    FrameEventHistoryStats(uint64_t fn, const sp<Fence>& gpuCompFence, CompositorTiming compTiming,
+                           nsecs_t refreshTime, nsecs_t dequeueReadyTime)
+          : frameNumber(fn),
+            gpuCompositionDoneFence(gpuCompFence),
+            compositorTiming(compTiming),
+            refreshStartTime(refreshTime),
+            dequeueReadyTime(dequeueReadyTime) {}
+
+    uint64_t frameNumber;
+    sp<Fence> gpuCompositionDoneFence;
+    CompositorTiming compositorTiming;
+    nsecs_t refreshStartTime;
+    nsecs_t dequeueReadyTime;
+};
+
 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) {}
+    SurfaceStats(const sp<IBinder>& sc, nsecs_t time, const sp<Fence>& prevReleaseFence,
+                 uint32_t hint, FrameEventHistoryStats frameEventStats)
+          : surfaceControl(sc),
+            acquireTime(time),
+            previousReleaseFence(prevReleaseFence),
+            transformHint(hint),
+            eventStats(frameEventStats) {}
 
     sp<IBinder> surfaceControl;
     nsecs_t acquireTime = -1;
     sp<Fence> previousReleaseFence;
+    uint32_t transformHint = 0;
+    FrameEventHistoryStats eventStats;
 };
 
 class TransactionStats : public Parcelable {
@@ -72,10 +102,10 @@
     status_t writeToParcel(Parcel* output) const override;
     status_t readFromParcel(const Parcel* input) override;
 
-    static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener,
+    static ListenerStats createEmpty(const sp<IBinder>& listener,
                                      const std::unordered_set<CallbackId>& callbackIds);
 
-    sp<ITransactionCompletedListener> listener;
+    sp<IBinder> listener;
     std::vector<TransactionStats> transactionStats;
 };
 
@@ -97,17 +127,59 @@
 
 class ListenerCallbacks {
 public:
-    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
-                      const std::unordered_set<CallbackId>& callbacks)
+    ListenerCallbacks(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbacks)
           : transactionCompletedListener(listener),
             callbackIds(callbacks.begin(), callbacks.end()) {}
 
-    ListenerCallbacks(const sp<ITransactionCompletedListener>& listener,
-                      const std::vector<CallbackId>& ids)
+    ListenerCallbacks(const sp<IBinder>& listener, const std::vector<CallbackId>& ids)
           : transactionCompletedListener(listener), callbackIds(ids) {}
 
-    sp<ITransactionCompletedListener> transactionCompletedListener;
+    bool operator==(const ListenerCallbacks& rhs) const {
+        if (transactionCompletedListener != rhs.transactionCompletedListener) {
+            return false;
+        }
+        if (callbackIds.empty()) {
+            return rhs.callbackIds.empty();
+        }
+        return callbackIds.front() == rhs.callbackIds.front();
+    }
+
+    sp<IBinder> transactionCompletedListener;
     std::vector<CallbackId> callbackIds;
 };
 
+struct IListenerHash {
+    std::size_t operator()(const sp<IBinder>& strongPointer) const {
+        return std::hash<IBinder*>{}(strongPointer.get());
+    }
+};
+
+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.empty()) ? 0 : callbackIds.front());
+    }
+};
+
+struct ListenerCallbacksHash {
+    std::size_t HashCombine(size_t value1, size_t value2) const {
+        return value1 ^ (value2 + 0x9e3779b9 + (value1 << 6) + (value1 >> 2));
+    }
+
+    std::size_t operator()(const ListenerCallbacks& listenerCallbacks) const {
+        struct IListenerHash listenerHasher;
+        struct CallbackIdsHash callbackIdsHasher;
+
+        std::size_t listenerHash = listenerHasher(listenerCallbacks.transactionCompletedListener);
+        std::size_t callbackIdsHash = callbackIdsHasher(listenerCallbacks.callbackIds);
+
+        return HashCombine(listenerHash, callbackIdsHash);
+    }
+};
+
 } // namespace android
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 47f0ced..d58e019 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -22,7 +22,12 @@
 
 namespace android {
 
-enum { METADATA_OWNER_UID = 1, METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3 };
+enum {
+    METADATA_OWNER_UID = 1,
+    METADATA_WINDOW_TYPE = 2,
+    METADATA_TASK_ID = 3,
+    METADATA_MOUSE_CURSOR = 4,
+};
 
 struct LayerMetadata : public Parcelable {
     std::unordered_map<uint32_t, std::vector<uint8_t>> mMap;
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index f438eb3..2d53b48 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -23,6 +23,7 @@
 #include <utils/Errors.h>
 
 #include <gui/IGraphicBufferProducer.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
 
 #ifndef NO_INPUT
@@ -34,6 +35,7 @@
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
+#include <ui/Rotation.h>
 
 namespace android {
 
@@ -71,7 +73,7 @@
         eCropChanged_legacy = 0x00000100,
         eDeferTransaction_legacy = 0x00000200,
         eOverrideScalingModeChanged = 0x00000400,
-        eGeometryAppliesWithResize = 0x00000800,
+        eShadowRadiusChanged = 0x00000800,
         eReparentChildren = 0x00001000,
         eDetachChildren = 0x00002000,
         eRelativeLayerChanged = 0x00004000,
@@ -97,6 +99,10 @@
         eBackgroundColorChanged = 0x4'00000000,
         eMetadataChanged = 0x8'00000000,
         eColorSpaceAgnosticChanged = 0x10'00000000,
+        eFrameRateSelectionPriority = 0x20'00000000,
+        eFrameRateChanged = 0x40'00000000,
+        eBackgroundBlurRadiusChanged = 0x80'00000000,
+        eProducerDisconnect = 0x100'00000000,
     };
 
     layer_state_t()
@@ -113,6 +119,7 @@
             reserved(0),
             crop_legacy(Rect::INVALID_RECT),
             cornerRadius(0.0f),
+            backgroundBlurRadius(0),
             frameNumber_legacy(0),
             overrideScalingMode(-1),
             transform(0),
@@ -123,10 +130,12 @@
             surfaceDamageRegion(),
             api(-1),
             colorTransform(mat4()),
-            hasListenerCallbacks(false),
             bgColorAlpha(0),
             bgColorDataspace(ui::Dataspace::UNKNOWN),
-            colorSpaceAgnostic(false) {
+            colorSpaceAgnostic(false),
+            shadowRadius(0.0f),
+            frameRateSelectionPriority(-1),
+            frameRate(0.0f) {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
         hdrMetadata.validTypes = 0;
@@ -157,6 +166,7 @@
     matrix22_t matrix;
     Rect crop_legacy;
     float cornerRadius;
+    uint32_t backgroundBlurRadius;
     sp<IBinder> barrierHandle_legacy;
     sp<IBinder> reparentHandle;
     uint64_t frameNumber_legacy;
@@ -186,7 +196,6 @@
     sp<NativeHandle> sidebandStream;
     mat4 colorTransform;
 
-    bool hasListenerCallbacks;
 #ifndef NO_INPUT
     InputWindowInfo inputInfo;
 #endif
@@ -203,10 +212,19 @@
     // A color space agnostic layer means the color of this layer can be
     // interpreted in any color space.
     bool colorSpaceAgnostic;
+
+    std::vector<ListenerCallbacks> listeners;
+
+    // Draws a shadow around the surface.
+    float shadowRadius;
+
+    // Priority of the layer assigned by Window Manager.
+    int32_t frameRateSelectionPriority;
+
+    float frameRate;
 };
 
 struct ComposerState {
-    sp<ISurfaceComposerClient> client;
     layer_state_t state;
     status_t write(Parcel& output) const;
     status_t read(const Parcel& input);
@@ -214,15 +232,6 @@
 
 struct DisplayState {
     enum {
-        eOrientationDefault = 0,
-        eOrientation90 = 1,
-        eOrientation180 = 2,
-        eOrientation270 = 3,
-        eOrientationUnchanged = 4,
-        eOrientationSwapMask = 0x01
-    };
-
-    enum {
         eSurfaceChanged = 0x01,
         eLayerStackChanged = 0x02,
         eDisplayProjectionChanged = 0x04,
@@ -248,7 +257,7 @@
     // 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;
+    ui::Rotation orientation = ui::ROTATION_0;
     Rect viewport;
     Rect frame;
 
@@ -274,8 +283,6 @@
 };
 
 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;
     return 0;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index a5641b0..4a353fc 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -21,16 +21,15 @@
 #include <gui/HdrMetadata.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/IProducerListener.h>
-
+#include <system/window.h>
 #include <ui/ANativeObjectBase.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Region.h>
-
 #include <utils/Condition.h>
 #include <utils/Mutex.h>
 #include <utils/RefBase.h>
 
-#include <system/window.h>
+#include <shared_mutex>
 
 namespace android {
 
@@ -179,8 +178,8 @@
     status_t getUniqueId(uint64_t* outId) const;
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
-    // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call
-    nsecs_t getLastDequeueStartTime() const;
+    // See IGraphicBufferProducer::setFrameRate
+    status_t setFrameRate(float frameRate);
 
 protected:
     virtual ~Surface();
@@ -205,6 +204,13 @@
             ANativeWindowBuffer* buffer, int fenceFd);
     static int hook_setSwapInterval(ANativeWindow* window, int interval);
 
+    static int cancelBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                    int fenceFd);
+    static int dequeueBufferInternal(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                     int* fenceFd);
+    static int performInternal(ANativeWindow* window, int operation, va_list args);
+    static int queueBufferInternal(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
+
     static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
             ANativeWindowBuffer* buffer);
     static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
@@ -246,6 +252,17 @@
     int dispatchGetWideColorSupport(va_list args);
     int dispatchGetHdrSupport(va_list args);
     int dispatchGetConsumerUsage64(va_list args);
+    int dispatchSetAutoPrerotation(va_list args);
+    int dispatchGetLastDequeueStartTime(va_list args);
+    int dispatchSetDequeueTimeout(va_list args);
+    int dispatchGetLastDequeueDuration(va_list args);
+    int dispatchGetLastQueueDuration(va_list args);
+    int dispatchSetFrameRate(va_list args);
+    int dispatchAddCancelInterceptor(va_list args);
+    int dispatchAddDequeueInterceptor(va_list args);
+    int dispatchAddPerformInterceptor(va_list args);
+    int dispatchAddQueueInterceptor(va_list args);
+    int dispatchGetLastQueuedBuffer(va_list args);
     bool transformToDisplayInverse();
 
 protected:
@@ -281,6 +298,7 @@
     virtual int setAsyncMode(bool async);
     virtual int setSharedBufferMode(bool sharedBufferMode);
     virtual int setAutoRefresh(bool autoRefresh);
+    virtual int setAutoPrerotation(bool autoPrerotation);
     virtual int setBuffersDimensions(uint32_t width, uint32_t height);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
@@ -450,6 +468,18 @@
     // member variables are accessed.
     mutable Mutex mMutex;
 
+    // mInterceptorMutex is the mutex guarding interceptors.
+    std::shared_mutex mInterceptorMutex;
+
+    ANativeWindow_cancelBufferInterceptor mCancelInterceptor = nullptr;
+    void* mCancelInterceptorData = nullptr;
+    ANativeWindow_dequeueBufferInterceptor mDequeueInterceptor = nullptr;
+    void* mDequeueInterceptorData = nullptr;
+    ANativeWindow_performInterceptor mPerformInterceptor = nullptr;
+    void* mPerformInterceptorData = nullptr;
+    ANativeWindow_queueBufferInterceptor mQueueInterceptor = nullptr;
+    void* mQueueInterceptorData = nullptr;
+
     // must be used from the lock/unlock thread
     sp<GraphicBuffer>           mLockedBuffer;
     sp<GraphicBuffer>           mPostedBuffer;
@@ -474,6 +504,7 @@
     // Caches the values that have been passed to the producer.
     bool mSharedBufferMode;
     bool mAutoRefresh;
+    bool mAutoPrerotation;
 
     // If in shared buffer mode and auto refresh is enabled, store the shared
     // buffer slot and return it for all calls to queue/dequeue without going
@@ -506,6 +537,7 @@
 
     bool mReportRemovedBuffers = false;
     std::vector<sp<GraphicBuffer>> mRemovedBuffers;
+    int mMaxBufferCount;
 
     sp<IProducerListener> mListenerProxy;
     status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots,
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0e17c7b..d0bb6a3 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
-#define ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -35,6 +34,7 @@
 #include <ui/FrameStats.h>
 #include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
+#include <ui/Rotation.h>
 
 #include <gui/CpuConsumer.h>
 #include <gui/ISurfaceComposer.h>
@@ -45,25 +45,31 @@
 
 namespace android {
 
-// ---------------------------------------------------------------------------
-
-struct DisplayInfo;
 class HdrCapabilities;
 class ISurfaceComposerClient;
 class IGraphicBufferProducer;
 class IRegionSamplingListener;
 class Region;
 
-// ---------------------------------------------------------------------------
-
 struct SurfaceControlStats {
-    SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t time,
-                        const sp<Fence>& prevReleaseFence)
-          : surfaceControl(sc), acquireTime(time), previousReleaseFence(prevReleaseFence) {}
+    SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
+                        const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
+                        uint32_t hint, FrameEventHistoryStats eventStats)
+          : surfaceControl(sc),
+            latchTime(latchTime),
+            acquireTime(acquireTime),
+            presentFence(presentFence),
+            previousReleaseFence(prevReleaseFence),
+            transformHint(hint),
+            frameEventStats(eventStats) {}
 
     sp<SurfaceControl> surfaceControl;
+    nsecs_t latchTime = -1;
     nsecs_t acquireTime = -1;
+    sp<Fence> presentFence;
     sp<Fence> previousReleaseFence;
+    uint32_t transformHint = 0;
+    FrameEventHistoryStats frameEventStats;
 };
 
 using TransactionCompletedCallbackTakesContext =
@@ -97,33 +103,36 @@
     status_t linkToComposerDeath(const sp<IBinder::DeathRecipient>& recipient,
             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,
-            Vector<DisplayInfo>* configs);
+    // Get transactional state of given display.
+    static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*);
 
-    // Get the DisplayInfo for the currently-active configuration
-    static status_t getDisplayInfo(const sp<IBinder>& display,
-            DisplayInfo* info);
+    // Get immutable information about given physical display.
+    static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*);
 
-    // Get the index of the current active configuration (relative to the list
-    // returned by getDisplayInfo)
+    // Get configurations supported by given physical display.
+    static status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>*);
+
+    // Get the ID of the active DisplayConfig, as getDisplayConfigs index.
     static int getActiveConfig(const sp<IBinder>& display);
 
-    // Set a new active configuration using an index relative to the list
-    // returned by getDisplayInfo
-    static status_t setActiveConfig(const sp<IBinder>& display, int id);
+    // Shorthand for getDisplayConfigs element at getActiveConfig index.
+    static status_t getActiveDisplayConfig(const sp<IBinder>& display, DisplayConfig*);
 
-    // Sets the allowed display configurations to be used.
-    // The allowedConfigs in a vector of indexes corresponding to the configurations
-    // returned from getDisplayConfigs().
-    static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                             const std::vector<int32_t>& allowedConfigs);
-
-    // Returns the allowed display configurations currently set.
-    // The allowedConfigs in a vector of indexes corresponding to the configurations
-    // returned from getDisplayConfigs().
-    static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                             std::vector<int32_t>* outAllowedConfigs);
+    // Sets the refresh rate boundaries for display configuration.
+    // For all other parameters, default configuration is used. The index for the default is
+    // corresponting to the configs returned from getDisplayConfigs().
+    static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                 int32_t defaultConfig, float minRefreshRate,
+                                                 float maxRefreshRate);
+    // Gets the refresh rate boundaries for display configuration.
+    // For all other parameters, default configuration is used. The index for the default is
+    // corresponting to the configs returned from getDisplayConfigs().
+    // The reason is passed in for telemetry tracking, and it corresponds to the list of all
+    // the policy rules that were used.
+    static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                 int32_t* outDefaultConfig,
+                                                 float* outMinRefreshRate,
+                                                 float* outMaxRefreshRate);
 
     // Gets the list of supported color modes for the given display
     static status_t getDisplayColorModes(const sp<IBinder>& display,
@@ -140,6 +149,21 @@
     static status_t setActiveColorMode(const sp<IBinder>& display,
             ui::ColorMode colorMode);
 
+    // Reports whether the connected display supports Auto Low Latency Mode
+    static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
+
+    // Switches on/off Auto Low Latency Mode on the connected display. This should only be
+    // called if the connected display supports Auto Low Latency Mode as reported by
+    // #getAutoLowLatencyModeSupport
+    static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
+
+    // Reports whether the connected display supports Game content type
+    static bool getGameContentTypeSupport(const sp<IBinder>& display);
+
+    // Turns Game mode on/off on the connected display. This should only be called
+    // if the display supports Game content type, as reported by #getGameContentTypeSupport
+    static void setGameContentType(const sp<IBinder>& display, bool on);
+
     /* Triggers screen on/off or low power mode and waits for it to complete */
     static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
 
@@ -163,8 +187,7 @@
      * 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);
+    static void doDropReferenceTransaction(const sp<IBinder>& handle);
 
     /**
      * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is
@@ -211,6 +234,27 @@
      */
     static status_t notifyPowerHint(int32_t hintId);
 
+    /*
+     * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+     * material design guidelines.
+     *
+     * ambientColor
+     *      Color to the ambient shadow. The alpha is premultiplied.
+     *
+     * spotColor
+     *      Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+     *      depends on the light position.
+     *
+     * lightPosY/lightPosZ
+     *      Position of the light used to cast the spot shadow. The X value is always the display
+     *      width / 2.
+     *
+     * lightRadius
+     *      Radius of the light casting the shadow.
+     */
+    static status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+                                            float lightPosY, float lightPosZ, float lightRadius);
+
     // ------------------------------------------------------------------------
     // surface creation / destruction
 
@@ -223,18 +267,18 @@
                                      PixelFormat format,               // pixel-format desired
                                      uint32_t flags = 0,               // usage flags
                                      SurfaceControl* parent = nullptr, // parent
-                                     LayerMetadata metadata = LayerMetadata() // metadata
-    );
+                                     LayerMetadata metadata = LayerMetadata(), // metadata
+                                     uint32_t* outTransformHint = nullptr);
 
     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
-    );
+                                  uint32_t flags = 0,                       // usage flags
+                                  SurfaceControl* parent = nullptr,         // parent
+                                  LayerMetadata metadata = LayerMetadata(), // metadata
+                                  uint32_t* outTransformHint = nullptr);
 
     //! Create a surface
     sp<SurfaceControl> createWithSurfaceParent(const String8& name,       // name of the surface
@@ -243,8 +287,19 @@
                                                PixelFormat format,        // pixel-format desired
                                                uint32_t flags = 0,        // usage flags
                                                Surface* parent = nullptr, // parent
-                                               LayerMetadata metadata = LayerMetadata() // metadata
-    );
+                                               LayerMetadata metadata = LayerMetadata(), // metadata
+                                               uint32_t* outTransformHint = nullptr);
+
+    // Creates a mirrored hierarchy for the mirrorFromSurface. This returns a SurfaceControl
+    // which is a parent of the root of the mirrored hierarchy.
+    //
+    //  Real Hierarchy    Mirror
+    //                      SC (value that's returned)
+    //                      |
+    //      A               A'
+    //      |               |
+    //      B               B'
+    sp<SurfaceControl> mirrorSurface(SurfaceControl* mirrorFromSurface);
 
     //! Create a virtual display
     static sp<IBinder> createDisplay(const String8& displayName, bool secure);
@@ -270,6 +325,12 @@
         }
     };
 
+    struct IBinderHash {
+        std::size_t operator()(const sp<IBinder>& iBinder) const {
+            return std::hash<IBinder*>{}(iBinder.get());
+        }
+    };
+
     struct TCLHash {
         std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const {
             return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr);
@@ -285,8 +346,9 @@
         std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls;
     };
 
-    class Transaction {
-        std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates;
+    class Transaction : public Parcelable {
+    protected:
+        std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
         SortedVector<DisplayState > mDisplayStates;
         std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
                 mListenerCallbacks;
@@ -314,7 +376,10 @@
         InputWindowCommands mInputWindowCommands;
         int mStatus = NO_ERROR;
 
-        layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
+        layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle);
+        layer_state_t* getLayerState(const sp<SurfaceControl>& sc) {
+            return getLayerState(sc->getHandle());
+        }
         DisplayState& getDisplayState(const sp<IBinder>& token);
 
         void cacheBuffers();
@@ -325,6 +390,15 @@
         virtual ~Transaction() = default;
         Transaction(Transaction const& other);
 
+        // Factory method that creates a new Transaction instance from the parcel.
+        static std::unique_ptr<Transaction> createFromParcel(const Parcel* parcel);
+
+        status_t writeToParcel(Parcel* parcel) const override;
+        status_t readFromParcel(const Parcel* parcel) override;
+
+        // Clears the contents of the transaction without applying it.
+        void clear();
+
         status_t apply(bool synchronous = false);
         // Merge another transaction in to this one, clearing other
         // as if it had been applied.
@@ -361,9 +435,10 @@
                 float dsdx, float dtdx, float dtdy, float dsdy);
         Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop);
         Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+        Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
+                                             int backgroundBlurRadius);
         Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack);
-        Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key,
-                                 std::vector<uint8_t> data);
+        Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p);
         // 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
@@ -410,9 +485,15 @@
         Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime);
         Transaction& setColorSpaceAgnostic(const sp<SurfaceControl>& sc, const bool agnostic);
 
+        // Sets information about the priority of the frame.
+        Transaction& setFrameRateSelectionPriority(const sp<SurfaceControl>& sc, int32_t priority);
+
         Transaction& addTransactionCompletedCallback(
                 TransactionCompletedCallbackTakesContext callback, void* callbackContext);
 
+        // ONLY FOR BLAST ADAPTER
+        Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc);
+
         // Detaches all child surfaces (and their children recursively)
         // from their SurfaceControl.
         // The child SurfaceControls will not throw exceptions or return errors,
@@ -430,12 +511,6 @@
         Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc,
                 int32_t overrideScalingMode);
 
-        // If the size changes in this transaction, all geometry updates specified
-        // in this transaction will not complete until a buffer of the new size
-        // arrives. As some elements normally apply immediately, this enables
-        // freezing the total geometry of a surface until a resize is completed.
-        Transaction& setGeometryAppliesWithResize(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);
@@ -448,6 +523,9 @@
 
         Transaction& setGeometry(const sp<SurfaceControl>& sc,
                 const Rect& source, const Rect& dst, int transform);
+        Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+
+        Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate);
 
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
@@ -464,10 +542,8 @@
          * mapped to. displayRect is specified post-orientation, that is
          * it uses the orientation seen by the end-user.
          */
-        void setDisplayProjection(const sp<IBinder>& token,
-                uint32_t orientation,
-                const Rect& layerStackRect,
-                const Rect& displayRect);
+        void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
+                                  const Rect& layerStackRect, const Rect& displayRect);
         void setDisplaySize(const sp<IBinder>& token, uint32_t width, uint32_t height);
         void setAnimationTransaction();
         void setEarlyWakeup();
@@ -481,10 +557,8 @@
     static status_t getHdrCapabilities(const sp<IBinder>& display,
             HdrCapabilities* outCapabilities);
 
-    static void setDisplayProjection(const sp<IBinder>& token,
-            uint32_t orientation,
-            const Rect& layerStackRect,
-            const Rect& displayRect);
+    static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation,
+                                     const Rect& layerStackRect, const Rect& displayRect);
 
     inline sp<ISurfaceComposerClient> getClient() { return mClient; }
 
@@ -516,23 +590,23 @@
 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, const ui::Dataspace reqDataSpace,
-                            const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+    static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+                            ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                             uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                            uint32_t rotation, bool captureSecureLayers,
+                            ui::Rotation rotation, bool captureSecureLayers,
                             sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers);
-    static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
-                            const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+    static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace,
+                            ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
                             uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
-                            uint32_t rotation, sp<GraphicBuffer>* outBuffer);
+                            ui::Rotation rotation, sp<GraphicBuffer>* outBuffer);
     static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
                             sp<GraphicBuffer>* outBuffer);
-    static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace,
-                                  const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
+    static status_t captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
+                                  ui::PixelFormat reqPixelFormat, const 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,
+            const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace,
+            ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
             const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
                     excludeHandles,
             float frameScale, sp<GraphicBuffer>* outBuffer);
@@ -551,15 +625,10 @@
 
     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<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash>
+                surfaceControls;
     };
 
     std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex);
@@ -582,8 +651,4 @@
     void onTransactionCompleted(ListenerStats stats) override;
 };
 
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_GUI_SURFACE_COMPOSER_CLIENT_H
+} // namespace android
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index 23bfc02..7bc7c68 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -44,7 +44,7 @@
 class SurfaceControl : public RefBase
 {
 public:
-    static sp<SurfaceControl> readFromParcel(Parcel* parcel);
+    static sp<SurfaceControl> readFromParcel(const Parcel* parcel);
     void writeToParcel(Parcel* parcel);
 
     static bool isValid(const sp<SurfaceControl>& surface) {
@@ -81,11 +81,15 @@
     status_t getLayerFrameStats(FrameStats* outStats) const;
 
     sp<SurfaceComposerClient> getClient() const;
-    
+
+    uint32_t getTransformHint() const;
+
+    void setTransformHint(uint32_t hint);
+
     explicit SurfaceControl(const sp<SurfaceControl>& other);
 
     SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle,
-                   const sp<IGraphicBufferProducer>& gbp, bool owned);
+                   const sp<IGraphicBufferProducer>& gbp, bool owned, uint32_t transformHint = 0);
 
 private:
     // can't be copied
@@ -106,6 +110,7 @@
     mutable Mutex               mLock;
     mutable sp<Surface>         mSurfaceData;
     bool                        mOwned;
+    uint32_t mTransformHint;
 };
 
 }; // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
index 627845c..811dcbe 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/Conversion.h
@@ -25,8 +25,6 @@
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
 
-#include <binder/Binder.h>
-#include <binder/Status.h>
 #include <ui/FenceTime.h>
 #include <cutils/native_handle.h>
 #include <gui/IGraphicBufferProducer.h>
@@ -127,15 +125,6 @@
  */
 
 /**
- * \brief Convert `Return<void>` to `binder::Status`.
- *
- * \param[in] t The source `Return<void>`.
- * \return The corresponding `binder::Status`.
- */
-// convert: Return<void> -> ::android::binder::Status
-::android::binder::Status toBinderStatus(Return<void> const& t);
-
-/**
  * \brief Convert `Return<void>` to `status_t`. This is for legacy binder calls.
  *
  * \param[in] t The source `Return<void>`.
diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
index 211fdd5..cda5103 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h
@@ -34,7 +34,12 @@
 using BProducerListener = ::android::IProducerListener;
 
 class H2BProducerListener
+#ifndef NO_BINDER
       : public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+      : public BProducerListener {
+    sp<HProducerListener> mBase;
+#endif
 public:
     H2BProducerListener(sp<HProducerListener> const& base);
     virtual void onBufferReleased() override;
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
index 029dcc0..99ab085 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h
@@ -49,7 +49,6 @@
 
 typedef ::android::IGraphicBufferProducer BGraphicBufferProducer;
 typedef ::android::IProducerListener BProducerListener;
-using ::android::BnGraphicBufferProducer;
 
 #ifndef LOG
 struct LOG_dummy {
diff --git a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
index 51dff5b..197db26 100644
--- a/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/1.0/WProducerListener.h
@@ -20,7 +20,6 @@
 #include <hidl/MQDescriptor.h>
 #include <hidl/Status.h>
 
-#include <binder/IBinder.h>
 #include <gui/IProducerListener.h>
 
 #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h>
@@ -55,6 +54,7 @@
     LWProducerListener(sp<HProducerListener> const& base);
     void onBufferReleased() override;
     bool needsReleaseNotify() override;
+    void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
 };
 
 }  // namespace android
diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
index 1c58167..16d054b 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h
@@ -45,6 +45,7 @@
 using ::android::hardware::hidl_vec;
 
 using ::android::hardware::graphics::common::V1_2::HardwareBuffer;
+struct Obituary;
 
 class B2HGraphicBufferProducer : public HGraphicBufferProducer {
 public:
@@ -108,6 +109,7 @@
 
 protected:
     sp<BGraphicBufferProducer> mBase;
+    sp<Obituary> mObituary;
 };
 
 
diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
index 898920b..92650b7 100644
--- a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
+++ b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h
@@ -33,12 +33,20 @@
 
 using BProducerListener = ::android::IProducerListener;
 
+#ifndef NO_BINDER
 class H2BProducerListener
       : public H2BConverter<HProducerListener, BnProducerListener> {
+#else
+class H2BProducerListener
+      : public BProducerListener {
+    sp<HProducerListener> mBase;
+
+#endif
 public:
     H2BProducerListener(sp<HProducerListener> const& base);
     virtual void onBufferReleased() override;
     virtual bool needsReleaseNotify() override;
+    virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) override;
 };
 
 }  // namespace utils
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
similarity index 95%
rename from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h
rename to libs/gui/include/gui/mock/GraphicBufferConsumer.h
index 98f24c2..e940cf3 100644
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
@@ -49,6 +49,8 @@
     MOCK_CONST_METHOD1(getSidebandStream, status_t(sp<NativeHandle>*));
     MOCK_METHOD2(getOccupancyHistory, status_t(bool, std::vector<OccupancyTracker::Segment>*));
     MOCK_METHOD0(discardFreeBuffers, status_t());
+    MOCK_METHOD1(setFrameRate, status_t(float));
+    MOCK_CONST_METHOD1(getFrameRate, status_t(float*));
     MOCK_CONST_METHOD2(dumpState, status_t(const String8&, String8*));
 };
 
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.h b/libs/gui/include/gui/mock/GraphicBufferProducer.h
similarity index 100%
rename from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.h
rename to libs/gui/include/gui/mock/GraphicBufferProducer.h
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp b/libs/gui/mock/GraphicBufferConsumer.cpp
similarity index 94%
rename from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp
rename to libs/gui/mock/GraphicBufferConsumer.cpp
index a17b73f..4a6c081 100644
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp
+++ b/libs/gui/mock/GraphicBufferConsumer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "mock/gui/MockGraphicBufferConsumer.h"
+#include <gui/mock/GraphicBufferConsumer.h>
 
 namespace android {
 namespace mock {
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp b/libs/gui/mock/GraphicBufferProducer.cpp
similarity index 94%
rename from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
rename to libs/gui/mock/GraphicBufferProducer.cpp
index a7fd667..239a80a 100644
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
+++ b/libs/gui/mock/GraphicBufferProducer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "mock/gui/MockGraphicBufferProducer.h"
+#include <gui/mock/GraphicBufferProducer.h>
 
 namespace android {
 namespace mock {
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index cbda6b2..7f960ab 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -13,7 +13,8 @@
     ],
 
     srcs: [
-        "BufferItemConsumer_test.cpp",
+        "BLASTBufferQueue_test.cpp",
+	"BufferItemConsumer_test.cpp",
         "BufferQueue_test.cpp",
         "CpuConsumer_test.cpp",
         "EndToEndNativeInputTest.cpp",
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
new file mode 100644
index 0000000..a87ccd6
--- /dev/null
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -0,0 +1,726 @@
+/*
+ * 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 "BLASTBufferQueue_test"
+
+#include <gui/BLASTBufferQueue.h>
+
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
+#include <gui/FrameTimestamps.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/IProducerListener.h>
+#include <gui/SurfaceComposerClient.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayConfig.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+#include <gtest/gtest.h>
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+
+class BLASTBufferQueueHelper {
+public:
+    BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) {
+        mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height);
+    }
+
+    void update(const sp<SurfaceControl>& sc, int width, int height) {
+        mBlastBufferQueueAdapter->update(sc, width, height);
+    }
+
+    void setNextTransaction(Transaction* next) {
+        mBlastBufferQueueAdapter->setNextTransaction(next);
+    }
+
+    int getWidth() { return mBlastBufferQueueAdapter->mWidth; }
+
+    int getHeight() { return mBlastBufferQueueAdapter->mHeight; }
+
+    Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
+
+    sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
+        return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
+    }
+
+    const sp<SurfaceControl> getSurfaceControl() {
+        return mBlastBufferQueueAdapter->mSurfaceControl;
+    }
+
+    void waitForCallbacks() {
+        std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+        while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
+            mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
+        }
+    }
+
+private:
+    sp<BLASTBufferQueue> mBlastBufferQueueAdapter;
+};
+
+class BLASTBufferQueueTest : public ::testing::Test {
+public:
+protected:
+    BLASTBufferQueueTest() {
+        const ::testing::TestInfo* const testInfo =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name());
+    }
+
+    ~BLASTBufferQueueTest() {
+        const ::testing::TestInfo* const testInfo =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGV("End test:   %s.%s", testInfo->test_case_name(), testInfo->name());
+    }
+
+    void SetUp() {
+        mComposer = ComposerService::getComposerService();
+        mClient = new SurfaceComposerClient();
+        mDisplayToken = mClient->getInternalDisplayToken();
+        ASSERT_NE(nullptr, mDisplayToken.get());
+        Transaction t;
+        t.setDisplayLayerStack(mDisplayToken, 0);
+        t.apply();
+        t.clear();
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &config));
+        const ui::Size& resolution = config.resolution;
+        mDisplayWidth = resolution.getWidth();
+        mDisplayHeight = resolution.getHeight();
+
+        mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth,
+                                                 mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                 /*parent*/ nullptr);
+        t.setLayerStack(mSurfaceControl, 0)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .setFrame(mSurfaceControl, Rect(resolution))
+                .show(mSurfaceControl)
+                .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB)
+                .apply();
+    }
+
+    void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) {
+        auto igbProducer = adapter.getIGraphicBufferProducer();
+        ASSERT_NE(nullptr, igbProducer.get());
+        ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2));
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        ASSERT_EQ(NO_ERROR,
+                  igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false,
+                                       &qbOutput));
+        ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+        producer = igbProducer;
+    }
+
+    void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
+                    uint8_t b) {
+        for (uint32_t row = rect.top; row < rect.bottom; row++) {
+            for (uint32_t col = rect.left; col < rect.right; col++) {
+                uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+                *pixel = r;
+                *(pixel + 1) = g;
+                *(pixel + 2) = b;
+                *(pixel + 3) = 255;
+            }
+        }
+    }
+
+    void fillQuadrants(sp<GraphicBuffer>& buf) {
+        const auto bufWidth = buf->getWidth();
+        const auto bufHeight = buf->getHeight();
+        uint32_t* bufData;
+        buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+                  reinterpret_cast<void**>(&bufData));
+        fillBuffer(bufData, Rect(0, 0, bufWidth / 2, bufHeight / 2), buf->getStride(), 0, 0, 0);
+        fillBuffer(bufData, Rect(bufWidth / 2, 0, bufWidth, bufHeight / 2), buf->getStride(), 255,
+                   0, 0);
+        fillBuffer(bufData, Rect(bufWidth / 2, bufHeight / 2, bufWidth, bufHeight),
+                   buf->getStride(), 0, 255, 0);
+        fillBuffer(bufData, Rect(0, bufHeight / 2, bufWidth / 2, bufHeight), buf->getStride(), 0, 0,
+                   255);
+        buf->unlock();
+    }
+
+    void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0,
+                            bool outsideRegion = false) {
+        const auto epsilon = 3;
+        const auto width = mScreenCaptureBuf->getWidth();
+        const auto height = mScreenCaptureBuf->getHeight();
+        const auto stride = mScreenCaptureBuf->getStride();
+
+        uint32_t* bufData;
+        mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
+                                reinterpret_cast<void**>(&bufData));
+
+        for (uint32_t row = 0; row < height; row++) {
+            for (uint32_t col = 0; col < width; col++) {
+                uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
+                bool inRegion;
+                if (!outsideRegion) {
+                    inRegion = row >= region.top + border && row < region.bottom - border &&
+                            col >= region.left + border && col < region.right - border;
+                } else {
+                    inRegion = row >= region.top - border && row < region.bottom + border &&
+                            col >= region.left - border && col < region.right + border;
+                }
+                if (!outsideRegion && inRegion) {
+                    EXPECT_GE(epsilon, abs(r - *(pixel)));
+                    EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+                    EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+                } else if (outsideRegion && !inRegion) {
+                    EXPECT_GE(epsilon, abs(r - *(pixel)));
+                    EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+                    EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+                }
+            }
+        }
+        mScreenCaptureBuf->unlock();
+        ASSERT_EQ(false, ::testing::Test::HasFailure());
+    }
+
+    sp<SurfaceComposerClient> mClient;
+    sp<ISurfaceComposer> mComposer;
+
+    sp<IBinder> mDisplayToken;
+
+    sp<SurfaceControl> mSurfaceControl;
+    sp<GraphicBuffer> mScreenCaptureBuf;
+
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+};
+
+TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) {
+    // create BLASTBufferQueue adapter associated with this surface
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
+    ASSERT_EQ(mDisplayWidth, adapter.getWidth());
+    ASSERT_EQ(mDisplayHeight, adapter.getHeight());
+    ASSERT_EQ(nullptr, adapter.getNextTransaction());
+}
+
+TEST_F(BLASTBufferQueueTest, Update) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<SurfaceControl> updateSurface =
+            mClient->createSurface(String8("UpdateTest"), mDisplayWidth / 2, mDisplayHeight / 2,
+                                   PIXEL_FORMAT_RGBA_8888);
+    adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2);
+    ASSERT_EQ(updateSurface, adapter.getSurfaceControl());
+    ASSERT_EQ(mDisplayWidth / 2, adapter.getWidth());
+    ASSERT_EQ(mDisplayHeight / 2, adapter.getHeight());
+}
+
+TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    Transaction next;
+    adapter.setNextTransaction(&next);
+    ASSERT_EQ(&next, adapter.getNextTransaction());
+}
+
+TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    nsecs_t desiredPresentTime = systemTime() + nsecs_t(5 * 1e8);
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(desiredPresentTime, false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    ASSERT_GE(systemTime(), desiredPresentTime);
+}
+
+TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, TripleBuffering) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+
+    std::vector<std::pair<int, sp<Fence>>> allocated;
+    for (int i = 0; i < 3; i++) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+        allocated.push_back({slot, fence});
+    }
+    for (int i = 0; i < allocated.size(); i++) {
+        igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
+    }
+
+    for (int i = 0; i < 100; i++) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(NO_ERROR, ret);
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+    }
+    adapter.waitForCallbacks();
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(mDisplayWidth, mDisplayHeight / 2),
+                                                   NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
+TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) {
+    uint8_t r = 255;
+    uint8_t g = 0;
+    uint8_t b = 0;
+
+    int32_t bufferSideLength =
+            (mDisplayWidth < mDisplayHeight) ? mDisplayWidth / 2 : mDisplayHeight / 2;
+    int32_t finalCropSideLength = bufferSideLength / 2;
+
+    auto bg = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                     ISurfaceComposerClient::eFXSurfaceEffect);
+    ASSERT_NE(nullptr, bg.get());
+    Transaction t;
+    t.setLayerStack(bg, 0)
+            .setCrop_legacy(bg, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setColor(bg, half3{0, 0, 0})
+            .setLayer(bg, 0)
+            .apply();
+
+    BLASTBufferQueueHelper adapter(mSurfaceControl, bufferSideLength, bufferSideLength);
+    sp<IGraphicBufferProducer> igbProducer;
+    setUpProducer(adapter, igbProducer);
+    int slot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buf;
+    auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufferSideLength, bufferSideLength,
+                                          PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                          nullptr, nullptr);
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+    ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+    uint32_t* bufData;
+    buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+              reinterpret_cast<void**>(&bufData));
+    fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight()), buf->getStride(), 0, 0, 0);
+    fillBuffer(bufData,
+               Rect(finalCropSideLength / 2, 0, buf->getWidth() - finalCropSideLength / 2,
+                    buf->getHeight()),
+               buf->getStride(), r, g, b);
+    buf->unlock();
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                   Rect(bufferSideLength, finalCropSideLength),
+                                                   NATIVE_WINDOW_SCALING_MODE_SCALE_CROP, 0,
+                                                   Fence::NO_FENCE);
+    igbProducer->queueBuffer(slot, input, &qbOutput);
+    ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+    adapter.waitForCallbacks();
+    // capture screen and verify that it is red
+    bool capturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                       ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(),
+                                       mDisplayWidth, mDisplayHeight,
+                                       /*useIdentityTransform*/ false));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(r, g, b,
+                               {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+    ASSERT_NO_FATAL_FAILURE(
+            checkScreenCapture(0, 0, 0,
+                               {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
+                               /*border*/ 0, /*outsideRegion*/ true));
+}
+
+class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
+public:
+    void test(uint32_t tr) {
+        BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+        sp<IGraphicBufferProducer> igbProducer;
+        setUpProducer(adapter, igbProducer);
+
+        auto bufWidth = mDisplayWidth;
+        auto bufHeight = mDisplayHeight;
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufWidth, bufHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+        fillQuadrants(buf);
+
+        IGraphicBufferProducer::QueueBufferOutput qbOutput;
+        IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(bufWidth, bufHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
+                                                       Fence::NO_FENCE);
+        igbProducer->queueBuffer(slot, input, &qbOutput);
+        ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint);
+
+        adapter.waitForCallbacks();
+        bool capturedSecureLayers;
+        ASSERT_EQ(NO_ERROR,
+                  mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+                                           ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+                                           Rect(), mDisplayWidth, mDisplayHeight,
+                                           /*useIdentityTransform*/ false));
+        switch (tr) {
+            case ui::Transform::ROT_0:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::FLIP_H:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::FLIP_V:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::ROT_90:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::ROT_180:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(255, 0, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+                break;
+            case ui::Transform::ROT_270:
+                ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+                                                           {0, 0, (int32_t)mDisplayWidth / 2,
+                                                            (int32_t)mDisplayHeight / 2},
+                                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 255, 0,
+                                           {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+                                            (int32_t)mDisplayHeight / 2},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 255,
+                                           {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+                                           1));
+                ASSERT_NO_FATAL_FAILURE(
+                        checkScreenCapture(0, 0, 0,
+                                           {0, (int32_t)mDisplayHeight / 2,
+                                            (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+                                           1));
+        }
+    }
+};
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_0) {
+    test(ui::Transform::ROT_0);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_H) {
+    test(ui::Transform::FLIP_H);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_V) {
+    test(ui::Transform::FLIP_V);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_90) {
+    test(ui::Transform::ROT_90);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_180) {
+    test(ui::Transform::ROT_180);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_270) {
+    test(ui::Transform::ROT_270);
+}
+
+class BLASTFrameEventHistoryTest : public BLASTBufferQueueTest {
+public:
+    void setUpAndQueueBuffer(const sp<IGraphicBufferProducer>& igbProducer,
+                             nsecs_t* requestedPresentTime, nsecs_t* postedTime,
+                             IGraphicBufferProducer::QueueBufferOutput* qbOutput,
+                             bool getFrameTimestamps) {
+        int slot;
+        sp<Fence> fence;
+        sp<GraphicBuffer> buf;
+        auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
+                                              PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                              nullptr, nullptr);
+        ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+        ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+        nsecs_t requestedTime = systemTime();
+        if (requestedPresentTime) *requestedPresentTime = requestedTime;
+        IGraphicBufferProducer::QueueBufferInput input(requestedTime, false, HAL_DATASPACE_UNKNOWN,
+                                                       Rect(mDisplayWidth, mDisplayHeight),
+                                                       NATIVE_WINDOW_SCALING_MODE_FREEZE, 0,
+                                                       Fence::NO_FENCE, /*sticky*/ 0,
+                                                       getFrameTimestamps);
+        if (postedTime) *postedTime = systemTime();
+        igbProducer->queueBuffer(slot, input, qbOutput);
+    }
+};
+
+TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) {
+    BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+    sp<IGraphicBufferProducer> igbProducer;
+    ProducerFrameEventHistory history;
+    setUpProducer(adapter, igbProducer);
+
+    IGraphicBufferProducer::QueueBufferOutput qbOutput;
+    nsecs_t requestedPresentTimeA = 0;
+    nsecs_t postedTimeA = 0;
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true);
+    history.applyDelta(qbOutput.frameTimestamps);
+
+    FrameEvents* events = nullptr;
+    events = history.getFrame(1);
+    ASSERT_NE(nullptr, events);
+    ASSERT_EQ(1, events->frameNumber);
+    ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+    ASSERT_GE(events->postedTime, postedTimeA);
+
+    adapter.waitForCallbacks();
+
+    // queue another buffer so we query for frame event deltas
+    nsecs_t requestedPresentTimeB = 0;
+    nsecs_t postedTimeB = 0;
+    setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true);
+    history.applyDelta(qbOutput.frameTimestamps);
+    events = history.getFrame(1);
+    ASSERT_NE(nullptr, events);
+
+    // frame number, requestedPresentTime, and postTime should not have changed
+    ASSERT_EQ(1, events->frameNumber);
+    ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
+    ASSERT_GE(events->postedTime, postedTimeA);
+
+    ASSERT_GE(events->latchTime, postedTimeA);
+    ASSERT_GE(events->dequeueReadyTime, events->latchTime);
+    ASSERT_NE(nullptr, events->gpuCompositionDoneFence);
+    ASSERT_NE(nullptr, events->displayPresentFence);
+    ASSERT_NE(nullptr, events->releaseFence);
+
+    // we should also have gotten the initial values for the next frame
+    events = history.getFrame(2);
+    ASSERT_NE(nullptr, events);
+    ASSERT_EQ(2, events->frameNumber);
+    ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
+    ASSERT_GE(events->postedTime, postedTimeB);
+}
+} // namespace android
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index 406f21f..6d7b6bb 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -169,6 +169,18 @@
     ASSERT_EQ(OK, item.mGraphicBuffer->unlock());
 }
 
+TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    mConsumer->consumerConnect(dc, false);
+    int bufferCount = 50;
+    mConsumer->setMaxBufferCount(bufferCount);
+
+    IGraphicBufferProducer::QueueBufferOutput output;
+    mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output);
+    ASSERT_EQ(output.maxBufferCount, bufferCount);
+}
+
 TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) {
     createBufferQueue();
     sp<DummyConsumer> dc(new DummyConsumer);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 03b9cd7..c59afba 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -41,7 +41,7 @@
 #include <input/InputTransport.h>
 #include <input/Input.h>
 
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
@@ -69,7 +69,6 @@
         mSurfaceControl = sc;
 
         InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel);
-        mServerChannel->setToken(new BBinder());
 
         mInputFlinger = getInputFlinger();
         mInputFlinger->registerInputChannel(mServerChannel);
@@ -83,7 +82,8 @@
                                                                int width, int height) {
         sp<SurfaceControl> surfaceControl =
                 scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
-                                   PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+                                   PIXEL_FORMAT_RGBA_8888,
+                                   ISurfaceComposerClient::eFXSurfaceEffect);
         return std::make_unique<InputSurface>(surfaceControl, width, height);
     }
 
@@ -113,22 +113,31 @@
         if (consumed != OK) {
             return nullptr;
         }
-        mInputConsumer->sendFinishedSignal(seqId, true);
+        status_t status = mInputConsumer->sendFinishedSignal(seqId, true);
+        EXPECT_EQ(OK, status) << "Could not send finished signal";
         return ev;
     }
 
+    void assertFocusChange(bool hasFocus) {
+        InputEvent *ev = consumeEvent();
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, ev->getType());
+        FocusEvent *focusEvent = static_cast<FocusEvent *>(ev);
+        EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+    }
+
     void expectTap(int x, int y) {
         InputEvent* ev = consumeEvent();
-        EXPECT_TRUE(ev != nullptr);
-        EXPECT_TRUE(ev->getType() == AINPUT_EVENT_TYPE_MOTION);
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
         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);
+        ASSERT_NE(ev, nullptr);
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, ev->getType());
         mev = static_cast<MotionEvent*>(ev);
         EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
     }
@@ -165,7 +174,7 @@
     }
 
     void populateInputInfo(int width, int height) {
-        mInputInfo.token = mServerChannel->getToken();
+        mInputInfo.token = mServerChannel->getConnectionToken();
         mInputInfo.name = "Test info";
         mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL;
         mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION;
@@ -213,15 +222,15 @@
         ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
 
         const auto display = mComposerClient->getInternalDisplayToken();
-        ASSERT_FALSE(display == nullptr);
+        ASSERT_NE(display, nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, mComposerClient->getDisplayInfo(display, &info));
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, mComposerClient->getActiveDisplayConfig(display, &config));
 
         // 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;
+        mBufferPostDelay = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
     }
 
     void TearDown() {
@@ -260,18 +269,28 @@
 TEST_F(InputSurfacesTest, can_receive_input) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->showAt(100, 100);
+    surface->assertFocusChange(true);
 
     injectTap(101, 101);
 
-    EXPECT_TRUE(surface->consumeEvent() != nullptr);
+    EXPECT_NE(surface->consumeEvent(), nullptr);
 }
 
+/**
+ * Set up two surfaces side-by-side. Tap each surface.
+ * Next, swap the positions of the two surfaces. Inject tap into the two
+ * original locations. Ensure that the tap is received by the surfaces in the
+ * reverse order.
+ */
 TEST_F(InputSurfacesTest, input_respects_positioning) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->showAt(100, 100);
+    surface->assertFocusChange(true);
 
     std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
     surface2->showAt(200, 200);
+    surface->assertFocusChange(false);
+    surface2->assertFocusChange(true);
 
     injectTap(201, 201);
     surface2->expectTap(1, 1);
@@ -298,11 +317,16 @@
     std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100);
 
     surface->showAt(10, 10);
+    surface->assertFocusChange(true);
     surface2->showAt(10, 10);
+    surface->assertFocusChange(false);
+    surface2->assertFocusChange(true);
 
     surface->doTransaction([](auto &t, auto &sc) {
          t.setLayer(sc, LAYER_BASE + 1);
     });
+    surface2->assertFocusChange(false);
+    surface->assertFocusChange(true);
 
     injectTap(11, 11);
     surface->expectTap(1, 1);
@@ -310,6 +334,8 @@
     surface2->doTransaction([](auto &t, auto &sc) {
          t.setLayer(sc, LAYER_BASE + 1);
     });
+    surface2->assertFocusChange(true);
+    surface->assertFocusChange(false);
 
     injectTap(11, 11);
     surface2->expectTap(1, 1);
@@ -317,6 +343,8 @@
     surface2->doTransaction([](auto &t, auto &sc) {
          t.hide(sc);
     });
+    surface2->assertFocusChange(false);
+    surface->assertFocusChange(true);
 
     injectTap(11, 11);
     surface->expectTap(1, 1);
@@ -329,9 +357,12 @@
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(100, 100);
+    bgSurface->assertFocusChange(true);
 
     fgSurface->mInputInfo.surfaceInset = 5;
     fgSurface->showAt(100, 100);
+    fgSurface->assertFocusChange(true);
+    bgSurface->assertFocusChange(false);
 
     injectTap(106, 106);
     fgSurface->expectTap(1, 1);
@@ -345,9 +376,12 @@
     std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100);
     parentSurface->showAt(100, 100);
+    parentSurface->assertFocusChange(true);
 
     childSurface->mInputInfo.surfaceInset = 10;
     childSurface->showAt(100, 100);
+    childSurface->assertFocusChange(true);
+    parentSurface->assertFocusChange(false);
 
     childSurface->doTransaction([&](auto &t, auto &sc) {
         t.setPosition(sc, -5, -5);
@@ -366,9 +400,12 @@
     std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
     bgSurface->showAt(100, 100);
+    bgSurface->assertFocusChange(true);
 
     fgSurface->mInputInfo.surfaceInset = 5;
     fgSurface->showAt(100, 100);
+    bgSurface->assertFocusChange(false);
+    fgSurface->assertFocusChange(true);
 
     fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
 
@@ -385,6 +422,7 @@
     // In case we pass the very big inset without any checking.
     fgSurface->mInputInfo.surfaceInset = INT32_MAX;
     fgSurface->showAt(100, 100);
+    fgSurface->assertFocusChange(true);
 
     fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
 
@@ -401,6 +439,7 @@
         t.setTransparentRegionHint(sc, transparentRegion);
     });
     surface->showAt(100, 100);
+    surface->assertFocusChange(true);
     injectTap(101, 101);
     surface->expectTap(1, 1);
 }
@@ -415,7 +454,10 @@
             InputSurface::makeBufferInputSurface(mComposerClient, 100, 100);
 
     bgSurface->showAt(10, 10);
+    bgSurface->assertFocusChange(true);
     bufferSurface->showAt(10, 10);
+    bgSurface->assertFocusChange(false);
+    bufferSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     bufferSurface->expectTap(1, 1);
@@ -432,7 +474,10 @@
     postBuffer(bufferSurface->mSurfaceControl);
 
     bgSurface->showAt(10, 10);
+    bgSurface->assertFocusChange(true);
     bufferSurface->showAt(10, 10);
+    bufferSurface->assertFocusChange(true);
+    bgSurface->assertFocusChange(false);
 
     injectTap(11, 11);
     bufferSurface->expectTap(1, 1);
@@ -448,7 +493,10 @@
     std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
 
     bgSurface->showAt(10, 10);
+    bgSurface->assertFocusChange(true);
     fgSurface->showAt(10, 10);
+    bgSurface->assertFocusChange(false);
+    fgSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     fgSurface->expectTap(1, 1);
@@ -465,12 +513,17 @@
             InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
 
     bgSurface->showAt(10, 10);
+    bgSurface->assertFocusChange(true);
     containerSurface->showAt(10, 10);
+    bgSurface->assertFocusChange(false);
+    containerSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     containerSurface->expectTap(1, 1);
 
     containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+    containerSurface->assertFocusChange(false);
+    bgSurface->assertFocusChange(true);
 
     injectTap(11, 11);
     bgSurface->expectTap(1, 1);
@@ -479,6 +532,7 @@
 TEST_F(InputSurfacesTest, input_respects_outscreen) {
     std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
     surface->showAt(-1, -1);
+    surface->assertFocusChange(true);
 
     injectTap(0, 0);
     surface->expectTap(1, 1);
diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp
index aef7aed..103f775 100644
--- a/libs/gui/tests/IGraphicBufferProducer_test.cpp
+++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp
@@ -543,7 +543,6 @@
     // Should now be able to dequeue up to minBuffers times
     DequeueBufferResult result;
     for (int i = 0; i < minBuffers; ++i) {
-
         EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION &
                 (dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT,
                               TEST_PRODUCER_USAGE_BITS, &result)))
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index d33ecfb..dbd4ef9 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -183,7 +183,7 @@
         mBackgroundLayer =
                 mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0,
                                                       0, PIXEL_FORMAT_RGBA_8888,
-                                                      ISurfaceComposerClient::eFXSurfaceColor);
+                                                      ISurfaceComposerClient::eFXSurfaceEffect);
         uint32_t layerPositionBottom = 0x7E000000;
         SurfaceComposerClient::Transaction{}
                 .setLayer(mBackgroundLayer, layerPositionBottom)
@@ -297,4 +297,70 @@
     composer->removeRegionSamplingListener(grayListener);
 }
 
+TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) {
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<Listener> listener = new Listener();
+    const Rect sampleArea{100, 100, 200, 200};
+    // Invalid input sampleArea
+    EXPECT_EQ(BAD_VALUE,
+              composer->addRegionSamplingListener(Rect::INVALID_RECT, mTopLayer->getHandle(),
+                                                  listener));
+    listener->reset();
+    // Invalid input binder
+    EXPECT_EQ(NO_ERROR, composer->addRegionSamplingListener(sampleArea, NULL, listener));
+    // Invalid input listener
+    EXPECT_EQ(BAD_VALUE,
+              composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), NULL));
+    EXPECT_EQ(BAD_VALUE, composer->removeRegionSamplingListener(NULL));
+    // remove the listener
+    composer->removeRegionSamplingListener(listener);
+}
+
+TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) {
+    fill_render(rgba_green);
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<Listener> listener = new Listener();
+    const Rect sampleArea{100, 100, 200, 200};
+    composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+    fill_render(rgba_green);
+
+    EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+    EXPECT_NEAR(listener->luma(), luma_green, error_margin);
+
+    listener->reset();
+    composer->removeRegionSamplingListener(listener);
+    fill_render(rgba_green);
+    EXPECT_FALSE(listener->wait_event(100ms))
+            << "callback should stop after remove the region sampling listener";
+}
+
+TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) {
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<Listener> listener = new Listener();
+    Rect sampleArea{100, 100, 200, 200};
+
+    // Test: listener in (100, 100). See layer before move, no layer after move.
+    fill_render(rgba_blue);
+    composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+    EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+    EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
+    listener->reset();
+    SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
+    EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+    EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
+    composer->removeRegionSamplingListener(listener);
+
+    // Test: listener offset to (600, 600). No layer before move, see layer after move.
+    fill_render(rgba_green);
+    sampleArea.offsetTo(600, 600);
+    composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+    EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+    EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
+    listener->reset();
+    SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
+    EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+    EXPECT_NEAR(listener->luma(), luma_green, error_margin);
+    composer->removeRegionSamplingListener(listener);
+}
+
 } // namespace android::test
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 9891587..5c1bebb 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -39,7 +39,7 @@
         sp<SurfaceComposerClient> client = new SurfaceComposerClient;
 
         mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                        ISurfaceComposerClient::eFXSurfaceColor);
+                                        ISurfaceComposerClient::eFXSurfaceEffect);
 
         const int32_t width = samplingArea.getWidth();
         const int32_t height = samplingArea.getHeight();
@@ -55,7 +55,7 @@
                 .apply();
 
         mButtonBlend = client->createSurface(String8(name) + "Blend", 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                             ISurfaceComposerClient::eFXSurfaceColor);
+                                             ISurfaceComposerClient::eFXSurfaceEffect);
 
         SurfaceComposerClient::Transaction{}
                 .setLayer(mButtonBlend, 0x7ffffffe)
@@ -73,7 +73,7 @@
         if (HIGHLIGHT_SAMPLING_AREA) {
             mSamplingArea =
                     client->createSurface(String8("SamplingArea"), 0, 0, PIXEL_FORMAT_RGBA_8888,
-                                          ISurfaceComposerClient::eFXSurfaceColor);
+                                          ISurfaceComposerClient::eFXSurfaceEffect);
 
             SurfaceComposerClient::Transaction{}
                     .setLayer(mSamplingArea, 0x7ffffffd)
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 65e09f2..c85e844 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -28,8 +28,6 @@
 #include <utils/Log.h>
 #include <utils/Thread.h>
 
-extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
-
 namespace android {
 
 class SurfaceTextureClientTest : public ::testing::Test {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a851687..70fd888 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -617,7 +617,7 @@
     anw->dequeueBuffer(anw.get(), &buffer, &fenceFd);
     nsecs_t after = systemTime(CLOCK_MONOTONIC);
 
-    nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime();
+    nsecs_t lastDequeueTime = ANativeWindow_getLastDequeueStartTime(anw.get());
     ASSERT_LE(before, lastDequeueTime);
     ASSERT_GE(after, lastDequeueTime);
 }
@@ -688,6 +688,7 @@
                              const sp<IBinder>& /*applyToken*/,
                              const InputWindowCommands& /*inputWindowCommands*/,
                              int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/,
+                             bool /*hasListenerCallbacks*/,
                              const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override {
     }
 
@@ -717,15 +718,18 @@
     }
 
     void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {}
-    status_t getDisplayConfigs(const sp<IBinder>& /*display*/,
-            Vector<DisplayInfo>* /*configs*/) override { return NO_ERROR; }
+    status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override {
+        return NO_ERROR;
+    }
+    status_t getDisplayConfigs(const sp<IBinder>& /*display*/, Vector<DisplayConfig>*) override {
+        return NO_ERROR;
+    }
+    status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override {
+        return NO_ERROR;
+    }
     status_t getDisplayStats(const sp<IBinder>& /*display*/,
             DisplayStatInfo* /*stats*/) override { return NO_ERROR; }
     int getActiveConfig(const sp<IBinder>& /*display*/) override { return 0; }
-    status_t setActiveConfig(const sp<IBinder>& /*display*/, int /*id*/)
-            override {
-        return NO_ERROR;
-    }
     status_t getDisplayColorModes(const sp<IBinder>& /*display*/,
             Vector<ColorMode>* /*outColorModes*/) override {
         return NO_ERROR;
@@ -741,21 +745,30 @@
     status_t setActiveColorMode(const sp<IBinder>& /*display*/,
         ColorMode /*colorMode*/) override { return NO_ERROR; }
     status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/,
-                           bool& /* outCapturedSecureLayers */,
-                           const ui::Dataspace /*reqDataspace*/,
-                           const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/,
+                           bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/,
+                           ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/,
                            uint32_t /*reqWidth*/, uint32_t /*reqHeight*/,
-                           bool /*useIdentityTransform*/, Rotation /*rotation*/,
+                           bool /*useIdentityTransform*/, ui::Rotation,
                            bool /*captureSecureLayers*/) override {
         return NO_ERROR;
     }
+    status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
+                                          bool* /*outSupport*/) const override {
+        return NO_ERROR;
+    }
+    void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
+    status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
+                                       bool* /*outSupport*/) const override {
+        return NO_ERROR;
+    }
+    void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
     status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
                            sp<GraphicBuffer>* /*outBuffer*/) override {
         return NO_ERROR;
     }
     virtual status_t captureLayers(
             const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/,
-            const ui::Dataspace /*reqDataspace*/, const ui::PixelFormat /*reqPixelFormat*/,
+            ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/,
             const Rect& /*sourceCrop*/,
             const std::unordered_set<sp<IBinder>,
                                      ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/,
@@ -822,16 +835,25 @@
             const sp<IRegionSamplingListener>& /*listener*/) override {
         return NO_ERROR;
     }
-    status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
-                                      const std::vector<int32_t>& /*allowedConfigs*/) override {
+    status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+                                          int32_t /*defaultConfig*/, float /*minRefreshRate*/,
+                                          float /*maxRefreshRate*/) {
         return NO_ERROR;
     }
-    status_t getAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
-                                      std::vector<int32_t>* /*outAllowedConfigs*/) override {
+    status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+                                          int32_t* /*outDefaultConfig*/,
+                                          float* /*outMinRefreshRate*/,
+                                          float* /*outMaxRefreshRate*/) override {
         return NO_ERROR;
-    }
+    };
     status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
 
+    status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
+                                     float /*lightPosY*/, float /*lightPosZ*/,
+                                     float /*lightRadius*/) override {
+        return NO_ERROR;
+    }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
@@ -1872,4 +1894,74 @@
     EXPECT_EQ(-1, outDisplayPresentTime);
 }
 
+TEST_F(SurfaceTest, DequeueWithConsumerDrivenSize) {
+    sp<IGraphicBufferProducer> producer;
+    sp<IGraphicBufferConsumer> consumer;
+    BufferQueue::createBufferQueue(&producer, &consumer);
+
+    sp<DummyConsumer> dummyConsumer(new DummyConsumer);
+    consumer->consumerConnect(dummyConsumer, false);
+    consumer->setDefaultBufferSize(10, 10);
+
+    sp<Surface> surface = new Surface(producer);
+    sp<ANativeWindow> window(surface);
+    native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+    native_window_set_buffers_dimensions(window.get(), 0, 0);
+
+    int fence;
+    ANativeWindowBuffer* buffer;
+
+    // Buffer size is driven by the consumer
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    EXPECT_EQ(10, buffer->width);
+    EXPECT_EQ(10, buffer->height);
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+    // Buffer size is driven by the consumer
+    consumer->setDefaultBufferSize(10, 20);
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    EXPECT_EQ(10, buffer->width);
+    EXPECT_EQ(20, buffer->height);
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+    // Transform hint isn't synced to producer before queueBuffer or connect
+    consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    EXPECT_EQ(10, buffer->width);
+    EXPECT_EQ(20, buffer->height);
+    ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence));
+
+    // Transform hint is synced to producer but no auto prerotation
+    consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    EXPECT_EQ(10, buffer->width);
+    EXPECT_EQ(20, buffer->height);
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+    // Prerotation is driven by the consumer with the transform hint used by producer
+    native_window_set_auto_prerotation(window.get(), true);
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    EXPECT_EQ(20, buffer->width);
+    EXPECT_EQ(10, buffer->height);
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+    // Turn off auto prerotaton
+    native_window_set_auto_prerotation(window.get(), false);
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    EXPECT_EQ(10, buffer->width);
+    EXPECT_EQ(20, buffer->height);
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+
+    // Test auto prerotation bit is disabled after disconnect
+    native_window_set_auto_prerotation(window.get(), true);
+    native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU);
+    native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU);
+    consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270);
+    native_window_set_buffers_dimensions(window.get(), 0, 0);
+    ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence));
+    EXPECT_EQ(10, buffer->width);
+    EXPECT_EQ(20, buffer->height);
+    ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence));
+}
+
 } // namespace android
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 2d78811..8efaf3d 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -48,6 +48,7 @@
                 "InputTransport.cpp",
                 "InputWindow.cpp",
                 "ISetInputWindowsListener.cpp",
+                "LatencyStatistics.cpp",
                 "VelocityControl.cpp",
                 "VelocityTracker.cpp",
             ],
@@ -55,7 +56,7 @@
             shared_libs: [
                 "libutils",
                 "libbinder",
-                "libui"
+                "libui",
             ],
 
             sanitize: {
diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp
index de3a23d..8ec5165 100644
--- a/libs/input/IInputFlinger.cpp
+++ b/libs/input/IInputFlinger.cpp
@@ -82,15 +82,13 @@
     }
     case REGISTER_INPUT_CHANNEL_TRANSACTION: {
         CHECK_INTERFACE(IInputFlinger, data, reply);
-        sp<InputChannel> channel = new InputChannel();
-        channel->read(data);
+        sp<InputChannel> channel = InputChannel::read(data);
         registerInputChannel(channel);
         break;
     }
     case UNREGISTER_INPUT_CHANNEL_TRANSACTION: {
         CHECK_INTERFACE(IInputFlinger, data, reply);
-        sp<InputChannel> channel = new InputChannel();
-        channel->read(data);
+        sp<InputChannel> channel = InputChannel::read(data);
         unregisterInputChannel(channel);
         break;
     }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 9fd25f9..2a73dc0 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -17,10 +17,10 @@
 #define LOG_TAG "Input"
 //#define LOG_NDEBUG 0
 
-#include <math.h>
 #include <limits.h>
 
 #include <input/Input.h>
+#include <input/InputDevice.h>
 #include <input/InputEventLabels.h>
 
 #ifdef __ANDROID__
@@ -42,16 +42,58 @@
 
 // --- InputEvent ---
 
-void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
+const char* inputEventTypeToString(int32_t type) {
+    switch (type) {
+        case AINPUT_EVENT_TYPE_KEY: {
+            return "KEY";
+        }
+        case AINPUT_EVENT_TYPE_MOTION: {
+            return "MOTION";
+        }
+        case AINPUT_EVENT_TYPE_FOCUS: {
+            return "FOCUS";
+        }
+    }
+    return "UNKNOWN";
+}
+
+VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
+    return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
+             event.getSource(), event.getDisplayId()},
+            event.getAction(),
+            event.getDownTime(),
+            event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+            event.getKeyCode(),
+            event.getScanCode(),
+            event.getMetaState(),
+            event.getRepeatCount()};
+}
+
+VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) {
+    return {{VerifiedInputEvent::Type::MOTION, event.getDeviceId(), event.getEventTime(),
+             event.getSource(), event.getDisplayId()},
+            event.getRawX(0),
+            event.getRawY(0),
+            event.getActionMasked(),
+            event.getDownTime(),
+            event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+            event.getMetaState(),
+            event.getButtonState()};
+}
+
+void InputEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                            std::array<uint8_t, 32> hmac) {
     mDeviceId = deviceId;
     mSource = source;
     mDisplayId = displayId;
+    mHmac = hmac;
 }
 
 void InputEvent::initialize(const InputEvent& from) {
     mDeviceId = from.mDeviceId;
     mSource = from.mSource;
     mDisplayId = from.mDisplayId;
+    mHmac = from.mHmac;
 }
 
 // --- KeyEvent ---
@@ -64,19 +106,11 @@
     return getKeyCodeByLabel(label);
 }
 
-void KeyEvent::initialize(
-        int32_t deviceId,
-        int32_t source,
-        int32_t displayId,
-        int32_t action,
-        int32_t flags,
-        int32_t keyCode,
-        int32_t scanCode,
-        int32_t metaState,
-        int32_t repeatCount,
-        nsecs_t downTime,
-        nsecs_t eventTime) {
-    InputEvent::initialize(deviceId, source, displayId);
+void KeyEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                          std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
+                          int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
+                          nsecs_t downTime, nsecs_t eventTime) {
+    InputEvent::initialize(deviceId, source, displayId, hmac);
     mAction = action;
     mFlags = flags;
     mKeyCode = keyCode;
@@ -235,27 +269,16 @@
 
 // --- MotionEvent ---
 
-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,
-        float yPrecision,
-        nsecs_t downTime,
-        nsecs_t eventTime,
-        size_t pointerCount,
-        const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords) {
-    InputEvent::initialize(deviceId, source, displayId);
+void MotionEvent::initialize(int32_t deviceId, uint32_t source, int32_t displayId,
+                             std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
+                             int32_t flags, int32_t edgeFlags, int32_t metaState,
+                             int32_t buttonState, MotionClassification classification, float xScale,
+                             float yScale, float xOffset, float yOffset, float xPrecision,
+                             float yPrecision, float rawXCursorPosition, float rawYCursorPosition,
+                             nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+                             const PointerProperties* pointerProperties,
+                             const PointerCoords* pointerCoords) {
+    InputEvent::initialize(deviceId, source, displayId, hmac);
     mAction = action;
     mActionButton = actionButton;
     mFlags = flags;
@@ -263,10 +286,14 @@
     mMetaState = metaState;
     mButtonState = buttonState;
     mClassification = classification;
+    mXScale = xScale;
+    mYScale = yScale;
     mXOffset = xOffset;
     mYOffset = yOffset;
     mXPrecision = xPrecision;
     mYPrecision = yPrecision;
+    mRawXCursorPosition = rawXCursorPosition;
+    mRawYCursorPosition = rawYCursorPosition;
     mDownTime = downTime;
     mPointerProperties.clear();
     mPointerProperties.appendArray(pointerProperties, pointerCount);
@@ -276,7 +303,7 @@
 }
 
 void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) {
-    InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId);
+    InputEvent::initialize(other->mDeviceId, other->mSource, other->mDisplayId, other->mHmac);
     mAction = other->mAction;
     mActionButton = other->mActionButton;
     mFlags = other->mFlags;
@@ -284,10 +311,14 @@
     mMetaState = other->mMetaState;
     mButtonState = other->mButtonState;
     mClassification = other->mClassification;
+    mXScale = other->mXScale;
+    mYScale = other->mYScale;
     mXOffset = other->mXOffset;
     mYOffset = other->mYOffset;
     mXPrecision = other->mXPrecision;
     mYPrecision = other->mYPrecision;
+    mRawXCursorPosition = other->mRawXCursorPosition;
+    mRawYCursorPosition = other->mRawYCursorPosition;
     mDownTime = other->mDownTime;
     mPointerProperties = other->mPointerProperties;
 
@@ -312,6 +343,21 @@
     mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
 }
 
+float MotionEvent::getXCursorPosition() const {
+    const float rawX = getRawXCursorPosition();
+    return rawX * mXScale + mXOffset;
+}
+
+float MotionEvent::getYCursorPosition() const {
+    const float rawY = getRawYCursorPosition();
+    return rawY * mYScale + mYOffset;
+}
+
+void MotionEvent::setCursorPosition(float x, float y) {
+    mRawXCursorPosition = (x - mXOffset) / mXScale;
+    mRawYCursorPosition = (y - mYOffset) / mYScale;
+}
+
 const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const {
     return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex];
 }
@@ -324,9 +370,9 @@
     float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis);
     switch (axis) {
     case AMOTION_EVENT_AXIS_X:
-        return value + mXOffset;
+        return value * mXScale + mXOffset;
     case AMOTION_EVENT_AXIS_Y:
-        return value + mYOffset;
+        return value * mYScale + mYOffset;
     }
     return value;
 }
@@ -346,9 +392,9 @@
     float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis);
     switch (axis) {
     case AMOTION_EVENT_AXIS_X:
-        return value + mXOffset;
+        return value * mXScale + mXOffset;
     case AMOTION_EVENT_AXIS_Y:
-        return value + mYOffset;
+        return value * mYScale + mYOffset;
     }
     return value;
 }
@@ -420,26 +466,35 @@
     float oldXOffset = mXOffset;
     float oldYOffset = mYOffset;
     float newX, newY;
-    float rawX = getRawX(0);
-    float rawY = getRawY(0);
-    transformPoint(matrix, rawX + oldXOffset, rawY + oldYOffset, &newX, &newY);
-    mXOffset = newX - rawX;
-    mYOffset = newY - rawY;
+    float scaledRawX = getRawX(0) * mXScale;
+    float scaledRawY = getRawY(0) * mYScale;
+    transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY);
+    mXOffset = newX - scaledRawX;
+    mYOffset = newY - scaledRawY;
 
     // Determine how the origin is transformed by the matrix so that we
     // can transform orientation vectors.
     float originX, originY;
     transformPoint(matrix, 0, 0, &originX, &originY);
 
+    // Apply the transformation to cursor position.
+    if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
+        float x = mRawXCursorPosition * mXScale + oldXOffset;
+        float y = mRawYCursorPosition * mYScale + oldYOffset;
+        transformPoint(matrix, x, y, &x, &y);
+        mRawXCursorPosition = (x - mXOffset) / mXScale;
+        mRawYCursorPosition = (y - mYOffset) / mYScale;
+    }
+
     // Apply the transformation to all samples.
     size_t numSamples = mSamplePointerCoords.size();
     for (size_t i = 0; i < numSamples; i++) {
         PointerCoords& c = mSamplePointerCoords.editItemAt(i);
-        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) + oldXOffset;
-        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) + oldYOffset;
+        float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset;
+        float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset;
         transformPoint(matrix, x, y, &x, &y);
-        c.setAxisValue(AMOTION_EVENT_AXIS_X, x - mXOffset);
-        c.setAxisValue(AMOTION_EVENT_AXIS_Y, y - mYOffset);
+        c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale);
+        c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale);
 
         float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
         c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
@@ -457,8 +512,14 @@
     }
 
     mDeviceId = parcel->readInt32();
-    mSource = parcel->readInt32();
+    mSource = parcel->readUint32();
     mDisplayId = parcel->readInt32();
+    std::vector<uint8_t> hmac;
+    status_t result = parcel->readByteVector(&hmac);
+    if (result != OK || hmac.size() != 32) {
+        return BAD_VALUE;
+    }
+    std::move(hmac.begin(), hmac.begin() + hmac.size(), mHmac.begin());
     mAction = parcel->readInt32();
     mActionButton = parcel->readInt32();
     mFlags = parcel->readInt32();
@@ -466,10 +527,14 @@
     mMetaState = parcel->readInt32();
     mButtonState = parcel->readInt32();
     mClassification = static_cast<MotionClassification>(parcel->readByte());
+    mXScale = parcel->readFloat();
+    mYScale = parcel->readFloat();
     mXOffset = parcel->readFloat();
     mYOffset = parcel->readFloat();
     mXPrecision = parcel->readFloat();
     mYPrecision = parcel->readFloat();
+    mRawXCursorPosition = parcel->readFloat();
+    mRawYCursorPosition = parcel->readFloat();
     mDownTime = parcel->readInt64();
 
     mPointerProperties.clear();
@@ -508,8 +573,10 @@
     parcel->writeInt32(sampleCount);
 
     parcel->writeInt32(mDeviceId);
-    parcel->writeInt32(mSource);
+    parcel->writeUint32(mSource);
     parcel->writeInt32(mDisplayId);
+    std::vector<uint8_t> hmac(mHmac.begin(), mHmac.end());
+    parcel->writeByteVector(hmac);
     parcel->writeInt32(mAction);
     parcel->writeInt32(mActionButton);
     parcel->writeInt32(mFlags);
@@ -517,10 +584,14 @@
     parcel->writeInt32(mMetaState);
     parcel->writeInt32(mButtonState);
     parcel->writeByte(static_cast<int8_t>(mClassification));
+    parcel->writeFloat(mXScale);
+    parcel->writeFloat(mYScale);
     parcel->writeFloat(mXOffset);
     parcel->writeFloat(mYOffset);
     parcel->writeFloat(mXPrecision);
     parcel->writeFloat(mYPrecision);
+    parcel->writeFloat(mRawXCursorPosition);
+    parcel->writeFloat(mRawYCursorPosition);
     parcel->writeInt64(mDownTime);
 
     for (size_t i = 0; i < pointerCount; i++) {
@@ -543,7 +614,7 @@
 }
 #endif
 
-bool MotionEvent::isTouchEvent(int32_t source, int32_t action) {
+bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
     if (source & AINPUT_SOURCE_CLASS_POINTER) {
         // Specifically excludes HOVER_MOVE and SCROLL.
         switch (action & AMOTION_EVENT_ACTION_MASK) {
@@ -568,6 +639,20 @@
     return getAxisByLabel(label);
 }
 
+// --- FocusEvent ---
+
+void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
+    InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+                           ADISPLAY_ID_NONE, INVALID_HMAC);
+    mHasFocus = hasFocus;
+    mInTouchMode = inTouchMode;
+}
+
+void FocusEvent::initialize(const FocusEvent& from) {
+    InputEvent::initialize(from);
+    mHasFocus = from.mHasFocus;
+    mInTouchMode = from.mInTouchMode;
+}
 
 // --- PooledInputEventFactory ---
 
@@ -576,43 +661,52 @@
 }
 
 PooledInputEventFactory::~PooledInputEventFactory() {
-    for (size_t i = 0; i < mKeyEventPool.size(); i++) {
-        delete mKeyEventPool.itemAt(i);
-    }
-    for (size_t i = 0; i < mMotionEventPool.size(); i++) {
-        delete mMotionEventPool.itemAt(i);
-    }
 }
 
 KeyEvent* PooledInputEventFactory::createKeyEvent() {
-    if (!mKeyEventPool.isEmpty()) {
-        KeyEvent* event = mKeyEventPool.top();
-        mKeyEventPool.pop();
-        return event;
+    if (mKeyEventPool.empty()) {
+        return new KeyEvent();
     }
-    return new KeyEvent();
+    KeyEvent* event = mKeyEventPool.front().release();
+    mKeyEventPool.pop();
+    return event;
 }
 
 MotionEvent* PooledInputEventFactory::createMotionEvent() {
-    if (!mMotionEventPool.isEmpty()) {
-        MotionEvent* event = mMotionEventPool.top();
-        mMotionEventPool.pop();
-        return event;
+    if (mMotionEventPool.empty()) {
+        return new MotionEvent();
     }
-    return new MotionEvent();
+    MotionEvent* event = mMotionEventPool.front().release();
+    mMotionEventPool.pop();
+    return event;
+}
+
+FocusEvent* PooledInputEventFactory::createFocusEvent() {
+    if (mFocusEventPool.empty()) {
+        return new FocusEvent();
+    }
+    FocusEvent* event = mFocusEventPool.front().release();
+    mFocusEventPool.pop();
+    return event;
 }
 
 void PooledInputEventFactory::recycle(InputEvent* event) {
     switch (event->getType()) {
     case AINPUT_EVENT_TYPE_KEY:
         if (mKeyEventPool.size() < mMaxPoolSize) {
-            mKeyEventPool.push(static_cast<KeyEvent*>(event));
+            mKeyEventPool.push(std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event)));
             return;
         }
         break;
     case AINPUT_EVENT_TYPE_MOTION:
         if (mMotionEventPool.size() < mMaxPoolSize) {
-            mMotionEventPool.push(static_cast<MotionEvent*>(event));
+            mMotionEventPool.push(std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event)));
+            return;
+        }
+        break;
+    case AINPUT_EVENT_TYPE_FOCUS:
+        if (mFocusEventPool.size() < mMaxPoolSize) {
+            mFocusEventPool.push(std::unique_ptr<FocusEvent>(static_cast<FocusEvent*>(event)));
             return;
         }
         break;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index d02cb8e..d25a5cc 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -11,10 +11,10 @@
 #define DEBUG_CHANNEL_MESSAGES 0
 
 // Log debug messages whenever InputChannel objects are created/destroyed
-#define DEBUG_CHANNEL_LIFECYCLE 0
+static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
 
 // Log debug messages about transport actions
-#define DEBUG_TRANSPORT_ACTIONS 0
+static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
 
 // Log debug messages about touch event resampling
 #define DEBUG_RESAMPLING 0
@@ -88,18 +88,23 @@
     return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
 }
 
+inline static const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
 // --- InputMessage ---
 
 bool InputMessage::isValid(size_t actualSize) const {
     if (size() == actualSize) {
         switch (header.type) {
-        case TYPE_KEY:
-            return true;
-        case TYPE_MOTION:
-            return body.motion.pointerCount > 0
-                    && body.motion.pointerCount <= MAX_POINTERS;
-        case TYPE_FINISHED:
-            return true;
+            case Type::KEY:
+                return true;
+            case Type::MOTION:
+                return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
+            case Type::FINISHED:
+                return true;
+            case Type::FOCUS:
+                return true;
         }
     }
     return false;
@@ -107,12 +112,14 @@
 
 size_t InputMessage::size() const {
     switch (header.type) {
-    case TYPE_KEY:
-        return sizeof(Header) + body.key.size();
-    case TYPE_MOTION:
-        return sizeof(Header) + body.motion.size();
-    case TYPE_FINISHED:
-        return sizeof(Header) + body.finished.size();
+        case Type::KEY:
+            return sizeof(Header) + body.key.size();
+        case Type::MOTION:
+            return sizeof(Header) + body.motion.size();
+        case Type::FINISHED:
+            return sizeof(Header) + body.finished.size();
+        case Type::FOCUS:
+            return sizeof(Header) + body.focus.size();
     }
     return sizeof(Header);
 }
@@ -129,7 +136,7 @@
 
     // Write the body
     switch(header.type) {
-        case InputMessage::TYPE_KEY: {
+        case InputMessage::Type::KEY: {
             // uint32_t seq
             msg->body.key.seq = body.key.seq;
             // nsecs_t eventTime
@@ -140,6 +147,8 @@
             msg->body.key.source = body.key.source;
             // int32_t displayId
             msg->body.key.displayId = body.key.displayId;
+            // std::array<uint8_t, 32> hmac
+            msg->body.key.hmac = body.key.hmac;
             // int32_t action
             msg->body.key.action = body.key.action;
             // int32_t flags
@@ -156,7 +165,7 @@
             msg->body.key.downTime = body.key.downTime;
             break;
         }
-        case InputMessage::TYPE_MOTION: {
+        case InputMessage::Type::MOTION: {
             // uint32_t seq
             msg->body.motion.seq = body.motion.seq;
             // nsecs_t eventTime
@@ -167,6 +176,8 @@
             msg->body.motion.source = body.motion.source;
             // int32_t displayId
             msg->body.motion.displayId = body.motion.displayId;
+            // std::array<uint8_t, 32> hmac
+            msg->body.motion.hmac = body.motion.hmac;
             // int32_t action
             msg->body.motion.action = body.motion.action;
             // int32_t actionButton
@@ -183,6 +194,10 @@
             msg->body.motion.edgeFlags = body.motion.edgeFlags;
             // nsecs_t downTime
             msg->body.motion.downTime = body.motion.downTime;
+            // float xScale
+            msg->body.motion.xScale = body.motion.xScale;
+            // float yScale
+            msg->body.motion.yScale = body.motion.yScale;
             // float xOffset
             msg->body.motion.xOffset = body.motion.xOffset;
             // float yOffset
@@ -191,6 +206,10 @@
             msg->body.motion.xPrecision = body.motion.xPrecision;
             // float yPrecision
             msg->body.motion.yPrecision = body.motion.yPrecision;
+            // float xCursorPosition
+            msg->body.motion.xCursorPosition = body.motion.xCursorPosition;
+            // float yCursorPosition
+            msg->body.motion.yCursorPosition = body.motion.yCursorPosition;
             // uint32_t pointerCount
             msg->body.motion.pointerCount = body.motion.pointerCount;
             //struct Pointer pointers[MAX_POINTERS]
@@ -208,13 +227,15 @@
             }
             break;
         }
-        case InputMessage::TYPE_FINISHED: {
+        case InputMessage::Type::FINISHED: {
             msg->body.finished.seq = body.finished.seq;
             msg->body.finished.handled = body.finished.handled;
             break;
         }
-        default: {
-            LOG_FATAL("Unexpected message type %i", header.type);
+        case InputMessage::Type::FOCUS: {
+            msg->body.focus.seq = body.focus.seq;
+            msg->body.focus.hasFocus = body.focus.hasFocus;
+            msg->body.focus.inTouchMode = body.focus.inTouchMode;
             break;
         }
     }
@@ -222,34 +243,27 @@
 
 // --- InputChannel ---
 
-InputChannel::InputChannel(const std::string& name, int fd) :
-        mName(name) {
-#if DEBUG_CHANNEL_LIFECYCLE
-    ALOGD("Input channel constructed: name='%s', fd=%d",
-            mName.c_str(), fd);
-#endif
+sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
+                                      sp<IBinder> token) {
+    const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
+    if (result != 0) {
+        LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
+                         strerror(errno));
+        return nullptr;
+    }
+    return new InputChannel(name, std::move(fd), token);
+}
 
-    setFd(fd);
+InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
+      : mName(name), mFd(std::move(fd)), mToken(token) {
+    if (DEBUG_CHANNEL_LIFECYCLE) {
+        ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
+    }
 }
 
 InputChannel::~InputChannel() {
-#if DEBUG_CHANNEL_LIFECYCLE
-    ALOGD("Input channel destroyed: name='%s', fd=%d",
-            mName.c_str(), mFd);
-#endif
-
-    ::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);
+    if (DEBUG_CHANNEL_LIFECYCLE) {
+        ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get());
     }
 }
 
@@ -271,13 +285,15 @@
     setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
     setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
 
-    std::string serverChannelName = name;
-    serverChannelName += " (server)";
-    outServerChannel = new InputChannel(serverChannelName, sockets[0]);
+    sp<IBinder> token = new BBinder();
 
-    std::string clientChannelName = name;
-    clientChannelName += " (client)";
-    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
+    std::string serverChannelName = name + " (server)";
+    android::base::unique_fd serverFd(sockets[0]);
+    outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
+
+    std::string clientChannelName = name + " (client)";
+    android::base::unique_fd clientFd(sockets[1]);
+    outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
     return OK;
 }
 
@@ -287,14 +303,14 @@
     msg->getSanitizedCopy(&cleanMsg);
     ssize_t nWrite;
     do {
-        nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+        nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
     } while (nWrite == -1 && errno == EINTR);
 
     if (nWrite < 0) {
         int error = errno;
 #if DEBUG_CHANNEL_MESSAGES
-        ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(),
-                msg->header.type, error);
+        ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
+              msg->header.type, strerror(error));
 #endif
         if (error == EAGAIN || error == EWOULDBLOCK) {
             return WOULD_BLOCK;
@@ -322,7 +338,7 @@
 status_t InputChannel::receiveMessage(InputMessage* msg) {
     ssize_t nRead;
     do {
-        nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
+        nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT);
     } while (nRead == -1 && errno == EINTR);
 
     if (nRead < 0) {
@@ -360,52 +376,53 @@
 }
 
 sp<InputChannel> InputChannel::dup() const {
-    int fd = ::dup(getFd());
-    return fd >= 0 ? new InputChannel(getName(), fd) : nullptr;
+    android::base::unique_fd newFd(::dup(getFd()));
+    if (!newFd.ok()) {
+        ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(),
+              strerror(errno));
+        const bool hitFdLimit = errno == EMFILE || errno == ENFILE;
+        // If this process is out of file descriptors, then throwing that might end up exploding
+        // on the other side of a binder call, which isn't really helpful.
+        // Better to just crash here and hope that the FD leak is slow.
+        // Other failures could be client errors, so we still propagate those back to the caller.
+        LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s",
+                            getName().c_str());
+        return nullptr;
+    }
+    return InputChannel::create(mName, std::move(newFd), mToken);
 }
 
-
 status_t InputChannel::write(Parcel& out) const {
-    status_t s = out.writeString8(String8(getName().c_str()));
-
+    status_t s = out.writeCString(getName().c_str());
     if (s != OK) {
         return s;
     }
+
     s = out.writeStrongBinder(mToken);
     if (s != OK) {
         return s;
     }
 
-    s = out.writeDupFileDescriptor(getFd());
-
+    s = out.writeUniqueFileDescriptor(mFd);
     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;
+sp<InputChannel> InputChannel::read(const Parcel& from) {
+    std::string name = from.readCString();
+    sp<IBinder> token = from.readStrongBinder();
+    android::base::unique_fd rawFd;
+    status_t fdResult = from.readUniqueFileDescriptor(&rawFd);
+    if (fdResult != OK) {
+        return nullptr;
     }
 
-    return OK;
+    return InputChannel::create(name, std::move(rawFd), token);
 }
 
-sp<IBinder> InputChannel::getToken() const {
+sp<IBinder> InputChannel::getConnectionToken() 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) :
@@ -415,32 +432,23 @@
 InputPublisher::~InputPublisher() {
 }
 
-status_t InputPublisher::publishKeyEvent(
-        uint32_t seq,
-        int32_t deviceId,
-        int32_t source,
-        int32_t displayId,
-        int32_t action,
-        int32_t flags,
-        int32_t keyCode,
-        int32_t scanCode,
-        int32_t metaState,
-        int32_t repeatCount,
-        nsecs_t downTime,
-        nsecs_t eventTime) {
+status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t deviceId, int32_t source,
+                                         int32_t displayId, std::array<uint8_t, 32> hmac,
+                                         int32_t action, int32_t flags, int32_t keyCode,
+                                         int32_t scanCode, int32_t metaState, int32_t repeatCount,
+                                         nsecs_t downTime, nsecs_t eventTime) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")",
                 mChannel->getName().c_str(), keyCode);
         ATRACE_NAME(message.c_str());
     }
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
-            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
-            "downTime=%" PRId64 ", eventTime=%" PRId64,
-            mChannel->getName().c_str(), seq,
-            deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
-            downTime, eventTime);
-#endif
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
+              "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+              "downTime=%" PRId64 ", eventTime=%" PRId64,
+              mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode,
+              metaState, repeatCount, downTime, eventTime);
+    }
 
     if (!seq) {
         ALOGE("Attempted to publish a key event with sequence number 0.");
@@ -448,11 +456,12 @@
     }
 
     InputMessage msg;
-    msg.header.type = InputMessage::TYPE_KEY;
+    msg.header.type = InputMessage::Type::KEY;
     msg.body.key.seq = seq;
     msg.body.key.deviceId = deviceId;
     msg.body.key.source = source;
     msg.body.key.displayId = displayId;
+    msg.body.key.hmac = hmac;
     msg.body.key.action = action;
     msg.body.key.flags = flags;
     msg.body.key.keyCode = keyCode;
@@ -465,44 +474,32 @@
 }
 
 status_t InputPublisher::publishMotionEvent(
-        uint32_t seq,
-        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,
-        float yPrecision,
-        nsecs_t downTime,
-        nsecs_t eventTime,
-        uint32_t pointerCount,
-        const PointerProperties* pointerProperties,
-        const PointerCoords* pointerCoords) {
+        uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId,
+        std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
+        int32_t edgeFlags, int32_t metaState, int32_t buttonState,
+        MotionClassification classification, float xScale, float yScale, float xOffset,
+        float yOffset, float xPrecision, float yPrecision, float xCursorPosition,
+        float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount,
+        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf(
                 "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")",
                 mChannel->getName().c_str(), action);
         ATRACE_NAME(message.c_str());
     }
-#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, classification=%s, xOffset=%f, yOffset=%f, "
-            "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
-            "pointerCount=%" PRIu32,
-            mChannel->getName().c_str(), seq,
-            deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
-            buttonState, motionClassificationToString(classification),
-            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
-#endif
+    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, classification=%s, xScale=%.1f, yScale=%.1f, "
+              "xOffset=%.1f, yOffset=%.1f, "
+              "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
+              "pointerCount=%" PRIu32,
+              mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
+              flags, edgeFlags, metaState, buttonState,
+              motionClassificationToString(classification), xScale, yScale, xOffset, yOffset,
+              xPrecision, yPrecision, downTime, eventTime, pointerCount);
+    }
 
     if (!seq) {
         ALOGE("Attempted to publish a motion event with sequence number 0.");
@@ -516,11 +513,12 @@
     }
 
     InputMessage msg;
-    msg.header.type = InputMessage::TYPE_MOTION;
+    msg.header.type = InputMessage::Type::MOTION;
     msg.body.motion.seq = seq;
     msg.body.motion.deviceId = deviceId;
     msg.body.motion.source = source;
     msg.body.motion.displayId = displayId;
+    msg.body.motion.hmac = hmac;
     msg.body.motion.action = action;
     msg.body.motion.actionButton = actionButton;
     msg.body.motion.flags = flags;
@@ -528,10 +526,14 @@
     msg.body.motion.metaState = metaState;
     msg.body.motion.buttonState = buttonState;
     msg.body.motion.classification = classification;
+    msg.body.motion.xScale = xScale;
+    msg.body.motion.yScale = yScale;
     msg.body.motion.xOffset = xOffset;
     msg.body.motion.yOffset = yOffset;
     msg.body.motion.xPrecision = xPrecision;
     msg.body.motion.yPrecision = yPrecision;
+    msg.body.motion.xCursorPosition = xCursorPosition;
+    msg.body.motion.yCursorPosition = yCursorPosition;
     msg.body.motion.downTime = downTime;
     msg.body.motion.eventTime = eventTime;
     msg.body.motion.pointerCount = pointerCount;
@@ -539,14 +541,31 @@
         msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
         msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
     }
+
+    return mChannel->sendMessage(&msg);
+}
+
+status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) {
+    if (ATRACE_ENABLED()) {
+        std::string message =
+                StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
+                             mChannel->getName().c_str(), toString(hasFocus),
+                             toString(inTouchMode));
+        ATRACE_NAME(message.c_str());
+    }
+
+    InputMessage msg;
+    msg.header.type = InputMessage::Type::FOCUS;
+    msg.body.focus.seq = seq;
+    msg.body.focus.hasFocus = hasFocus ? 1 : 0;
+    msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
     return mChannel->sendMessage(&msg);
 }
 
 status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
-            mChannel->getName().c_str());
-#endif
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+    }
 
     InputMessage msg;
     status_t result = mChannel->receiveMessage(&msg);
@@ -555,13 +574,13 @@
         *outHandled = false;
         return result;
     }
-    if (msg.header.type != InputMessage::TYPE_FINISHED) {
+    if (msg.header.type != InputMessage::Type::FINISHED) {
         ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
                 mChannel->getName().c_str(), msg.header.type);
         return UNKNOWN_ERROR;
     }
     *outSeq = msg.body.finished.seq;
-    *outHandled = msg.body.finished.handled;
+    *outHandled = msg.body.finished.handled == 1;
     return OK;
 }
 
@@ -579,12 +598,12 @@
     return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
 }
 
-status_t InputConsumer::consume(InputEventFactoryInterface* factory,
-        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
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, 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(), toString(consumeBatches), frameTime);
+    }
 
     *outSeq = 0;
     *outEvent = nullptr;
@@ -604,10 +623,10 @@
                 if (consumeBatches || result != WOULD_BLOCK) {
                     result = consumeBatch(factory, frameTime, outSeq, outEvent);
                     if (*outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
-                        ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
-                                mChannel->getName().c_str(), *outSeq);
-#endif
+                        if (DEBUG_TRANSPORT_ACTIONS) {
+                            ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
+                                  mChannel->getName().c_str(), *outSeq);
+                        }
                         break;
                     }
                 }
@@ -616,92 +635,103 @@
         }
 
         switch (mMsg.header.type) {
-        case InputMessage::TYPE_KEY: {
-            KeyEvent* keyEvent = factory->createKeyEvent();
-            if (!keyEvent) return NO_MEMORY;
+            case InputMessage::Type::KEY: {
+                KeyEvent* keyEvent = factory->createKeyEvent();
+                if (!keyEvent) return NO_MEMORY;
 
-            initializeKeyEvent(keyEvent, &mMsg);
-            *outSeq = mMsg.body.key.seq;
-            *outEvent = keyEvent;
-#if DEBUG_TRANSPORT_ACTIONS
-            ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
-                    mChannel->getName().c_str(), *outSeq);
-#endif
-            break;
-        }
-
-        case InputMessage::TYPE_MOTION: {
-            ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
-            if (batchIndex >= 0) {
-                Batch& batch = mBatches.editItemAt(batchIndex);
-                if (canAddSample(batch, &mMsg)) {
-                    batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
-                    ALOGD("channel '%s' consumer ~ appended to batch event",
-                            mChannel->getName().c_str());
-#endif
-                    break;
-                } else if (isPointerEvent(mMsg.body.motion.source) &&
-                        mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
-                    // No need to process events that we are going to cancel anyways
-                    const size_t count = batch.samples.size();
-                    for (size_t i = 0; i < count; i++) {
-                        const InputMessage& msg = batch.samples.itemAt(i);
-                        sendFinishedSignal(msg.body.motion.seq, false);
-                    }
-                    batch.samples.removeItemsAt(0, count);
-                    mBatches.removeAt(batchIndex);
-                } else {
-                    // We cannot append to the batch in progress, so we need to consume
-                    // 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);
-                    mBatches.removeAt(batchIndex);
-                    if (result) {
-                        return result;
-                    }
-#if DEBUG_TRANSPORT_ACTIONS
-                    ALOGD("channel '%s' consumer ~ consumed batch event and "
-                            "deferred current event, seq=%u",
-                            mChannel->getName().c_str(), *outSeq);
-#endif
-                    break;
+                initializeKeyEvent(keyEvent, &mMsg);
+                *outSeq = mMsg.body.key.seq;
+                *outEvent = keyEvent;
+                if (DEBUG_TRANSPORT_ACTIONS) {
+                    ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+                          mChannel->getName().c_str(), *outSeq);
                 }
+            break;
             }
 
-            // Start a new batch if needed.
-            if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
-                    || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                mBatches.push();
-                Batch& batch = mBatches.editTop();
-                batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
-                ALOGD("channel '%s' consumer ~ started batch event",
-                        mChannel->getName().c_str());
-#endif
+            case InputMessage::Type::MOTION: {
+                ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
+                if (batchIndex >= 0) {
+                    Batch& batch = mBatches.editItemAt(batchIndex);
+                    if (canAddSample(batch, &mMsg)) {
+                        batch.samples.push(mMsg);
+                        if (DEBUG_TRANSPORT_ACTIONS) {
+                            ALOGD("channel '%s' consumer ~ appended to batch event",
+                                  mChannel->getName().c_str());
+                        }
+                    break;
+                    } else if (isPointerEvent(mMsg.body.motion.source) &&
+                               mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
+                        // No need to process events that we are going to cancel anyways
+                        const size_t count = batch.samples.size();
+                        for (size_t i = 0; i < count; i++) {
+                            const InputMessage& msg = batch.samples.itemAt(i);
+                            sendFinishedSignal(msg.body.motion.seq, false);
+                        }
+                        batch.samples.removeItemsAt(0, count);
+                        mBatches.removeAt(batchIndex);
+                    } else {
+                        // We cannot append to the batch in progress, so we need to consume
+                        // 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);
+                        mBatches.removeAt(batchIndex);
+                        if (result) {
+                            return result;
+                        }
+                        if (DEBUG_TRANSPORT_ACTIONS) {
+                            ALOGD("channel '%s' consumer ~ consumed batch event and "
+                                  "deferred current event, seq=%u",
+                                  mChannel->getName().c_str(), *outSeq);
+                        }
+                    break;
+                    }
+                }
+
+                // Start a new batch if needed.
+                if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
+                    mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                    mBatches.push();
+                    Batch& batch = mBatches.editTop();
+                    batch.samples.push(mMsg);
+                    if (DEBUG_TRANSPORT_ACTIONS) {
+                        ALOGD("channel '%s' consumer ~ started batch event",
+                              mChannel->getName().c_str());
+                    }
+                    break;
+                }
+
+                MotionEvent* motionEvent = factory->createMotionEvent();
+                if (!motionEvent) return NO_MEMORY;
+
+                updateTouchState(mMsg);
+                initializeMotionEvent(motionEvent, &mMsg);
+                *outSeq = mMsg.body.motion.seq;
+                *outEvent = motionEvent;
+
+                if (DEBUG_TRANSPORT_ACTIONS) {
+                    ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
+                          mChannel->getName().c_str(), *outSeq);
+                }
                 break;
             }
 
-            MotionEvent* motionEvent = factory->createMotionEvent();
-            if (! motionEvent) return NO_MEMORY;
+            case InputMessage::Type::FINISHED: {
+                LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by "
+                                 "InputConsumer!");
+                break;
+            }
 
-            updateTouchState(mMsg);
-            initializeMotionEvent(motionEvent, &mMsg);
-            *outSeq = mMsg.body.motion.seq;
-            *outEvent = motionEvent;
+            case InputMessage::Type::FOCUS: {
+                FocusEvent* focusEvent = factory->createFocusEvent();
+                if (!focusEvent) return NO_MEMORY;
 
-#if DEBUG_TRANSPORT_ACTIONS
-            ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
-                    mChannel->getName().c_str(), *outSeq);
-#endif
-            break;
-        }
-
-        default:
-            ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
-                    mChannel->getName().c_str(), mMsg.header.type);
-            return UNKNOWN_ERROR;
+                initializeFocusEvent(focusEvent, &mMsg);
+                *outSeq = mMsg.body.focus.seq;
+                *outEvent = focusEvent;
+                break;
+            }
         }
     }
     return OK;
@@ -1026,10 +1056,10 @@
 }
 
 status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
-#if DEBUG_TRANSPORT_ACTIONS
-    ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
-            mChannel->getName().c_str(), seq, handled ? "true" : "false");
-#endif
+    if (DEBUG_TRANSPORT_ACTIONS) {
+        ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+              mChannel->getName().c_str(), seq, toString(handled));
+    }
 
     if (!seq) {
         ALOGE("Attempted to send a finished signal with sequence number 0.");
@@ -1076,9 +1106,9 @@
 
 status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
     InputMessage msg;
-    msg.header.type = InputMessage::TYPE_FINISHED;
+    msg.header.type = InputMessage::Type::FINISHED;
     msg.body.finished.seq = seq;
-    msg.body.finished.handled = handled;
+    msg.body.finished.handled = handled ? 1 : 0;
     return mChannel->sendMessage(&msg);
 }
 
@@ -1112,18 +1142,14 @@
 }
 
 void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) {
-    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,
-            msg->body.key.scanCode,
-            msg->body.key.metaState,
-            msg->body.key.repeatCount,
-            msg->body.key.downTime,
-            msg->body.key.eventTime);
+    event->initialize(msg->body.key.deviceId, msg->body.key.source, msg->body.key.displayId,
+                      msg->body.key.hmac, msg->body.key.action, msg->body.key.flags,
+                      msg->body.key.keyCode, msg->body.key.scanCode, msg->body.key.metaState,
+                      msg->body.key.repeatCount, msg->body.key.downTime, msg->body.key.eventTime);
+}
+
+void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
+    event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1);
 }
 
 void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
@@ -1135,26 +1161,16 @@
         pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords);
     }
 
-    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,
-            msg->body.motion.yPrecision,
-            msg->body.motion.downTime,
-            msg->body.motion.eventTime,
-            pointerCount,
-            pointerProperties,
-            pointerCoords);
+    event->initialize(msg->body.motion.deviceId, msg->body.motion.source,
+                      msg->body.motion.displayId, msg->body.motion.hmac, 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.xScale, msg->body.motion.yScale, msg->body.motion.xOffset,
+                      msg->body.motion.yOffset, msg->body.motion.xPrecision,
+                      msg->body.motion.yPrecision, msg->body.motion.xCursorPosition,
+                      msg->body.motion.yCursorPosition, msg->body.motion.downTime,
+                      msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
 }
 
 void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index ec28757..03ca459 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -73,6 +73,7 @@
     status_t s = output.writeStrongBinder(token);
     if (s != OK) return s;
 
+    output.writeInt32(id);
     output.writeString8(String8(name.c_str()));
     output.writeInt32(layoutParamsFlags);
     output.writeInt32(layoutParamsType);
@@ -90,7 +91,6 @@
     output.writeBool(hasFocus);
     output.writeBool(hasWallpaper);
     output.writeBool(paused);
-    output.writeInt32(layer);
     output.writeInt32(ownerPid);
     output.writeInt32(ownerUid);
     output.writeInt32(inputFeatures);
@@ -116,6 +116,7 @@
     }
 
     ret.token = token;
+    ret.id = from.readInt32();
     ret.name = from.readString8().c_str();
     ret.layoutParamsFlags = from.readInt32();
     ret.layoutParamsType = from.readInt32();
@@ -133,7 +134,6 @@
     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();
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index e189d20..6f9b162 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -487,9 +487,9 @@
         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, ADISPLAY_ID_NONE,
-            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
-            0, keyCode, 0, metaState, 0, time, time);
+    event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState,
+                     0, time, time);
 }
 
 void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp
index 0c22bfe..56900c1 100644
--- a/libs/input/Keyboard.cpp
+++ b/libs/input/Keyboard.cpp
@@ -38,29 +38,29 @@
 KeyMap::~KeyMap() {
 }
 
-status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
+status_t KeyMap::load(const InputDeviceIdentifier& deviceIdentifier,
         const PropertyMap* deviceConfiguration) {
     // Use the configured key layout if available.
     if (deviceConfiguration) {
         String8 keyLayoutName;
         if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"),
                 keyLayoutName)) {
-            status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str());
+            status_t status = loadKeyLayout(deviceIdentifier, 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.c_str(), keyLayoutName.string());
+                        deviceIdentifier.name.c_str(), keyLayoutName.string());
             }
         }
 
         String8 keyCharacterMapName;
         if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"),
                 keyCharacterMapName)) {
-            status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str());
+            status_t status = loadKeyCharacterMap(deviceIdentifier, 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.c_str(), keyLayoutName.string());
+                        deviceIdentifier.name.c_str(), keyCharacterMapName.string());
             }
         }
 
@@ -70,25 +70,25 @@
     }
 
     // Try searching by device identifier.
-    if (probeKeyMap(deviceIdenfifier, "")) {
+    if (probeKeyMap(deviceIdentifier, "")) {
         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, "Generic")) {
+    if (probeKeyMap(deviceIdentifier, "Generic")) {
         return OK;
     }
 
     // Try the Virtual key map as a last resort.
-    if (probeKeyMap(deviceIdenfifier, "Virtual")) {
+    if (probeKeyMap(deviceIdentifier, "Virtual")) {
         return OK;
     }
 
     // Give up!
     ALOGE("Could not determine key map for device '%s' and no default key maps were found!",
-            deviceIdenfifier.name.c_str());
+            deviceIdentifier.name.c_str());
     return NAME_NOT_FOUND;
 }
 
diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp
new file mode 100644
index 0000000..394da22
--- /dev/null
+++ b/libs/input/LatencyStatistics.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 <input/LatencyStatistics.h>
+
+#include <android-base/chrono_utils.h>
+
+#include <cmath>
+#include <limits>
+
+namespace android {
+
+LatencyStatistics::LatencyStatistics(std::chrono::seconds period) : mReportPeriod(period) {
+    reset();
+}
+
+/**
+ * Add a raw value to the statistics
+ */
+void LatencyStatistics::addValue(float value) {
+    if (value < mMin) {
+        mMin = value;
+    }
+    if (value > mMax) {
+        mMax = value;
+    }
+    mSum += value;
+    mSum2 += value * value;
+    mCount++;
+}
+
+/**
+ * Get the mean. Should not be called if no samples have been added.
+ */
+float LatencyStatistics::getMean() {
+    return mSum / mCount;
+}
+
+/**
+ * Get the standard deviation. Should not be called if no samples have been added.
+ */
+float LatencyStatistics::getStDev() {
+    float mean = getMean();
+    return sqrt(mSum2 / mCount - mean * mean);
+}
+
+float LatencyStatistics::getMin() {
+    return mMin;
+}
+
+float LatencyStatistics::getMax() {
+    return mMax;
+}
+
+size_t LatencyStatistics::getCount() {
+    return mCount;
+}
+
+/**
+ * Reset internal state. The variable 'when' is the time when the data collection started.
+ * Call this to start a new data collection window.
+ */
+void LatencyStatistics::reset() {
+    mMax = std::numeric_limits<float>::lowest();
+    mMin = std::numeric_limits<float>::max();
+    mSum = 0;
+    mSum2 = 0;
+    mCount = 0;
+    mLastReportTime = std::chrono::steady_clock::now();
+}
+
+bool LatencyStatistics::shouldReport() {
+    std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime;
+    return mCount != 0 && timeSinceReport >= mReportPeriod;
+}
+
+} // namespace android
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index 8a4298a..145b4ae 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <input/DisplayViewport.h>
 #include <input/TouchVideoFrame.h>
 
 namespace android {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index ade931e..fb21d5e 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -7,14 +7,15 @@
         "InputEvent_test.cpp",
         "InputPublisherAndConsumer_test.cpp",
         "InputWindow_test.cpp",
+        "LatencyStatistics_test.cpp",
         "TouchVideoFrame_test.cpp",
         "VelocityTracker_test.cpp",
+        "VerifiedInputEvent_test.cpp",
     ],
     cflags: [
         "-Wall",
         "-Wextra",
         "-Werror",
-        "-Wno-unused-variable",
     ],
     shared_libs: [
         "libinput",
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index f1675c0..ada275d 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -22,11 +22,12 @@
 #include <time.h>
 #include <errno.h>
 
+#include <binder/Binder.h>
 #include <gtest/gtest.h>
 #include <input/InputTransport.h>
-#include <utils/Timers.h>
 #include <utils/StopWatch.h>
 #include <utils/StrongPointer.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -43,20 +44,27 @@
     // of a pipe and to check for EPIPE on the other end after the channel is destroyed.
     Pipe pipe;
 
-    sp<InputChannel> inputChannel = new InputChannel("channel name", pipe.sendFd);
+    android::base::unique_fd sendFd(pipe.sendFd);
 
+    sp<InputChannel> inputChannel =
+            InputChannel::create("channel name", std::move(sendFd), new BBinder());
+
+    EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created";
     EXPECT_STREQ("channel name", inputChannel->getName().c_str())
             << "channel should have provided name";
-    EXPECT_EQ(pipe.sendFd, inputChannel->getFd())
-            << "channel should have provided fd";
+    EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd";
 
-    inputChannel.clear(); // destroys input channel
+    // InputChannel should be the owner of the file descriptor now
+    ASSERT_FALSE(sendFd.ok());
+}
 
-    EXPECT_EQ(-EPIPE, pipe.readSignal())
-            << "channel should have closed fd when destroyed";
+TEST_F(InputChannelTest, SetAndGetToken) {
+    Pipe pipe;
+    sp<IBinder> token = new BBinder();
+    sp<InputChannel> channel =
+            InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token);
 
-    // clean up fds of Pipe endpoints that were closed so we don't try to close them again
-    pipe.sendFd = -1;
+    EXPECT_EQ(token, channel->getConnectionToken());
 }
 
 TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) {
@@ -77,7 +85,7 @@
     // Server->Client communication
     InputMessage serverMsg;
     memset(&serverMsg, 0, sizeof(InputMessage));
-    serverMsg.header.type = InputMessage::TYPE_KEY;
+    serverMsg.header.type = InputMessage::Type::KEY;
     serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN;
     EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg))
             << "server channel should be able to send message to client channel";
@@ -93,7 +101,7 @@
     // Client->Server communication
     InputMessage clientReply;
     memset(&clientReply, 0, sizeof(InputMessage));
-    clientReply.header.type = InputMessage::TYPE_FINISHED;
+    clientReply.header.type = InputMessage::Type::FINISHED;
     clientReply.body.finished.seq = 0x11223344;
     clientReply.body.finished.handled = true;
     EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply))
@@ -152,7 +160,7 @@
     serverChannel.clear(); // close server channel
 
     InputMessage msg;
-    msg.header.type = InputMessage::TYPE_KEY;
+    msg.header.type = InputMessage::Type::KEY;
     EXPECT_EQ(DEAD_OBJECT, clientChannel->sendMessage(&msg))
             << "sendMessage should have returned DEAD_OBJECT";
 }
@@ -171,7 +179,7 @@
     };
 
     InputMessage serverMsg = {}, clientMsg;
-    serverMsg.header.type = InputMessage::TYPE_MOTION;
+    serverMsg.header.type = InputMessage::Type::MOTION;
     serverMsg.body.motion.seq = 1;
     serverMsg.body.motion.pointerCount = 1;
 
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 2b75c82..d0f7618 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -26,7 +26,12 @@
 // Default display id.
 static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
 
-class BaseTest : public testing::Test { };
+class BaseTest : public testing::Test {
+protected:
+    static constexpr std::array<uint8_t, 32> HMAC = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+                                                     11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+                                                     22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+};
 
 // --- PointerCoordsTest ---
 
@@ -41,7 +46,6 @@
 }
 
 TEST_F(PointerCoordsTest, AxisValues) {
-    float* valuePtr;
     PointerCoords coords;
     coords.clear();
 
@@ -176,16 +180,17 @@
     KeyEvent event;
 
     // Initialize and get properties.
-    const nsecs_t ARBITRARY_DOWN_TIME = 1;
-    const nsecs_t ARBITRARY_EVENT_TIME = 2;
-    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);
+    constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+    constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+    event.initialize(2, AINPUT_SOURCE_GAMEPAD, DISPLAY_ID, HMAC, 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(AINPUT_SOURCE_GAMEPAD, event.getSource());
     ASSERT_EQ(DISPLAY_ID, event.getDisplayId());
+    EXPECT_EQ(HMAC, event.getHmac());
     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,7 +202,7 @@
 
     // Set source.
     event.setSource(AINPUT_SOURCE_JOYSTICK);
-    ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+    ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
 
     // Set display id.
     constexpr int32_t newDisplayId = 2;
@@ -210,19 +215,17 @@
 
 class MotionEventTest : public BaseTest {
 protected:
-    static const nsecs_t ARBITRARY_DOWN_TIME;
-    static const nsecs_t ARBITRARY_EVENT_TIME;
-    static const float X_OFFSET;
-    static const float Y_OFFSET;
+    static constexpr nsecs_t ARBITRARY_DOWN_TIME = 1;
+    static constexpr nsecs_t ARBITRARY_EVENT_TIME = 2;
+    static constexpr float X_SCALE = 2.0;
+    static constexpr float Y_SCALE = 3.0;
+    static constexpr float X_OFFSET = 1;
+    static constexpr float Y_OFFSET = 1.1;
 
     void initializeEventWithHistory(MotionEvent* event);
     void assertEqualsEventWithHistory(const MotionEvent* event);
 };
 
-const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1;
-const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2;
-const float MotionEventTest::X_OFFSET = 1.0f;
-const float MotionEventTest::Y_OFFSET = 1.1f;
 
 void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
     PointerProperties pointerProperties[2];
@@ -254,12 +257,13 @@
     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, 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,
-            MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
-            ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME,
-            2, pointerProperties, pointerCoords);
+    event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0,
+                      AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP,
+                      AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE,
+                      X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f,
+                      AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                      ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties,
+                      pointerCoords);
 
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110);
     pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111);
@@ -306,14 +310,17 @@
     // Check properties.
     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(AINPUT_SOURCE_TOUCHSCREEN, event->getSource());
     ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
+    EXPECT_EQ(HMAC, event->getHmac());
     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());
+    EXPECT_EQ(X_SCALE, event->getXScale());
+    EXPECT_EQ(Y_SCALE, event->getYScale());
     ASSERT_EQ(X_OFFSET, event->getXOffset());
     ASSERT_EQ(Y_OFFSET, event->getYOffset());
     ASSERT_EQ(2.0f, event->getXPrecision());
@@ -367,19 +374,19 @@
     ASSERT_EQ(211, event->getRawY(0));
     ASSERT_EQ(221, event->getRawY(1));
 
-    ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0));
-    ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0));
-    ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1));
-    ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1));
-    ASSERT_EQ(X_OFFSET + 210, event->getX(0));
-    ASSERT_EQ(X_OFFSET + 220, event->getX(1));
+    ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0));
+    ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0));
+    ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1));
+    ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1));
+    ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0));
+    ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1));
 
-    ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0));
-    ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0));
-    ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1));
-    ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1));
-    ASSERT_EQ(Y_OFFSET + 211, event->getY(0));
-    ASSERT_EQ(Y_OFFSET + 221, event->getY(1));
+    ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0));
+    ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0));
+    ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1));
+    ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1));
+    ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0));
+    ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1));
 
     ASSERT_EQ(12, event->getHistoricalPressure(0, 0));
     ASSERT_EQ(22, event->getHistoricalPressure(1, 0));
@@ -440,7 +447,7 @@
 
     // Set source.
     event.setSource(AINPUT_SOURCE_JOYSTICK);
-    ASSERT_EQ(static_cast<int>(AINPUT_SOURCE_JOYSTICK), event.getSource());
+    ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
 
     // Set displayId.
     constexpr int32_t newDisplayId = 2;
@@ -505,8 +512,8 @@
 
     ASSERT_EQ(210 * 2, event.getRawX(0));
     ASSERT_EQ(211 * 2, event.getRawY(0));
-    ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0));
-    ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0));
+    ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0));
+    ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0));
     ASSERT_EQ(212, event.getPressure(0));
     ASSERT_EQ(213, event.getSize(0));
     ASSERT_EQ(214 * 2, event.getTouchMajor(0));
@@ -570,11 +577,13 @@
         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle);
     }
     MotionEvent event;
-    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);
+    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
+                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+                     MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/,
+                     0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
+                     3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/,
+                     0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
     float originalRawX = 0 + 3;
     float originalRawY = -RADIUS + 2;
 
@@ -602,6 +611,14 @@
         ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1);
     }
 
+    // Check cursor positions. The original cursor position is at (3 + RADIUS, 2), where the center
+    // of the circle is (3, 2), so the cursor position is to the right of the center of the circle.
+    // The choice of triangular functions in this test defines the angle of rotation clockwise
+    // relative to the y-axis. Therefore the cursor position's angle is 90 degrees. Here we swap the
+    // triangular function so that we don't have to add the 90 degrees.
+    ASSERT_NEAR(cosf(PI_180 * ROTATION) * RADIUS, event.getXCursorPosition(), 0.001);
+    ASSERT_NEAR(sinf(PI_180 * ROTATION) * RADIUS, event.getYCursorPosition(), 0.001);
+
     // Applying the transformation should preserve the raw X and Y of the first point.
     ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001);
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
@@ -625,12 +642,47 @@
     }
 
     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);
+        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
+                         AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
+                         0, classification, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/,
+                         pointerCount, pointerProperties, pointerCoords);
         ASSERT_EQ(classification, event.getClassification());
     }
 }
 
+TEST_F(MotionEventTest, Initialize_SetsCursorPosition) {
+    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();
+    }
+
+    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0,
+                     MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, 0,
+                     280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/,
+                     0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords);
+    event.offsetLocation(20, 60);
+    ASSERT_EQ(280, event.getRawXCursorPosition());
+    ASSERT_EQ(540, event.getRawYCursorPosition());
+    ASSERT_EQ(300, event.getXCursorPosition());
+    ASSERT_EQ(600, event.getYCursorPosition());
+}
+
+TEST_F(MotionEventTest, SetCursorPosition) {
+    MotionEvent event;
+    initializeEventWithHistory(&event);
+    event.setSource(AINPUT_SOURCE_MOUSE);
+
+    event.setCursorPosition(3, 4);
+    ASSERT_EQ(3, event.getXCursorPosition());
+    ASSERT_EQ(4, event.getYCursorPosition());
+}
+
 } // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index f2cd1be..885196f 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -38,6 +38,7 @@
     virtual void SetUp() {
         status_t result = InputChannel::openInputChannelPair("channel name",
                 serverChannel, clientChannel);
+        ASSERT_EQ(OK, result);
 
         mPublisher = new InputPublisher(serverChannel);
         mConsumer = new InputConsumer(clientChannel);
@@ -60,6 +61,7 @@
 
     void PublishAndConsumeKeyEvent();
     void PublishAndConsumeMotionEvent();
+    void PublishAndConsumeFocusEvent();
 };
 
 TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -72,8 +74,11 @@
 
     constexpr uint32_t seq = 15;
     constexpr int32_t deviceId = 1;
-    constexpr int32_t source = AINPUT_SOURCE_KEYBOARD;
+    constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
     constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+    constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+                                              20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,
+                                              9,  8,  7,  6,  5,  4,  3,  2,  1,  0};
     constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
     constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
     constexpr int32_t keyCode = AKEYCODE_ENTER;
@@ -83,8 +88,9 @@
     constexpr nsecs_t downTime = 3;
     constexpr nsecs_t eventTime = 4;
 
-    status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, action, flags,
-            keyCode, scanCode, metaState, repeatCount, downTime, eventTime);
+    status = mPublisher->publishKeyEvent(seq, deviceId, source, displayId, hmac, action, flags,
+                                         keyCode, scanCode, metaState, repeatCount, downTime,
+                                         eventTime);
     ASSERT_EQ(OK, status)
             << "publisher publishKeyEvent should return OK";
 
@@ -104,6 +110,7 @@
     EXPECT_EQ(deviceId, keyEvent->getDeviceId());
     EXPECT_EQ(source, keyEvent->getSource());
     EXPECT_EQ(displayId, keyEvent->getDisplayId());
+    EXPECT_EQ(hmac, keyEvent->getHmac());
     EXPECT_EQ(action, keyEvent->getAction());
     EXPECT_EQ(flags, keyEvent->getFlags());
     EXPECT_EQ(keyCode, keyEvent->getKeyCode());
@@ -133,8 +140,11 @@
 
     constexpr uint32_t seq = 15;
     constexpr int32_t deviceId = 1;
-    constexpr int32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+    constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
     constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+    constexpr std::array<uint8_t, 32> hmac = {0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
+                                              11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+                                              22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
     constexpr int32_t action = AMOTION_EVENT_ACTION_MOVE;
     constexpr int32_t actionButton = 0;
     constexpr int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
@@ -142,10 +152,14 @@
     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 xScale = 2;
+    constexpr float yScale = 3;
     constexpr float xOffset = -10;
     constexpr float yOffset = -20;
     constexpr float xPrecision = 0.25;
     constexpr float yPrecision = 0.5;
+    constexpr float xCursorPosition = 1.3;
+    constexpr float yCursorPosition = 50.6;
     constexpr nsecs_t downTime = 3;
     constexpr size_t pointerCount = 3;
     constexpr nsecs_t eventTime = 4;
@@ -168,10 +182,12 @@
         pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
     }
 
-    status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton,
-            flags, edgeFlags, metaState, buttonState, classification,
-            xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount,
-            pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, hmac, action,
+                                            actionButton, flags, edgeFlags, metaState, buttonState,
+                                            classification, xScale, yScale, xOffset, yOffset,
+                                            xPrecision, yPrecision, xCursorPosition,
+                                            yCursorPosition, downTime, eventTime, pointerCount,
+                                            pointerProperties, pointerCoords);
     ASSERT_EQ(OK, status)
             << "publisher publishMotionEvent should return OK";
 
@@ -191,14 +207,23 @@
     EXPECT_EQ(deviceId, motionEvent->getDeviceId());
     EXPECT_EQ(source, motionEvent->getSource());
     EXPECT_EQ(displayId, motionEvent->getDisplayId());
+    EXPECT_EQ(hmac, motionEvent->getHmac());
     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(xScale, motionEvent->getXScale());
+    EXPECT_EQ(yScale, motionEvent->getYScale());
+    EXPECT_EQ(xOffset, motionEvent->getXOffset());
+    EXPECT_EQ(yOffset, motionEvent->getYOffset());
     EXPECT_EQ(xPrecision, motionEvent->getXPrecision());
     EXPECT_EQ(yPrecision, motionEvent->getYPrecision());
+    EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition());
+    EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition());
+    EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition());
+    EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition());
     EXPECT_EQ(downTime, motionEvent->getDownTime());
     EXPECT_EQ(eventTime, motionEvent->getEventTime());
     EXPECT_EQ(pointerCount, motionEvent->getPointerCount());
@@ -213,10 +238,10 @@
                 motionEvent->getRawX(i));
         EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
                 motionEvent->getRawY(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset,
-                motionEvent->getX(i));
-        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset,
-                motionEvent->getY(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset,
+                  motionEvent->getX(i));
+        EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset,
+                  motionEvent->getY(i));
         EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
                 motionEvent->getPressure(i));
         EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
@@ -248,6 +273,43 @@
             << "publisher receiveFinishedSignal should have set handled to consumer's reply";
 }
 
+void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+    status_t status;
+
+    constexpr uint32_t seq = 15;
+    constexpr bool hasFocus = true;
+    constexpr bool inTouchMode = true;
+
+    status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode);
+    ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+    uint32_t consumeSeq;
+    InputEvent* event;
+    status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+    ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+    ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+    ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+            << "consumer should have returned a focus event";
+
+    FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+    EXPECT_EQ(seq, consumeSeq);
+    EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+    EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
+
+    status = mConsumer->sendFinishedSignal(seq, true);
+    ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+    uint32_t finishedSeq = 0;
+    bool handled = false;
+    status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+    ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
+    ASSERT_EQ(seq, finishedSeq)
+            << "publisher receiveFinishedSignal should have returned the original sequence number";
+    ASSERT_TRUE(handled)
+            << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
@@ -256,6 +318,10 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
 }
 
+TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+}
+
 TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
     status_t status;
     const size_t pointerCount = 1;
@@ -266,9 +332,12 @@
         pointerCoords[i].clear();
     }
 
-    status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(0, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+                                            MotionClassification::NONE, 1 /* xScale */,
+                                            1 /* yScale */, 0, 0, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -279,9 +348,12 @@
     PointerProperties pointerProperties[pointerCount];
     PointerCoords pointerCoords[pointerCount];
 
-    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+                                            MotionClassification::NONE, 1 /* xScale */,
+                                            1 /* yScale */, 0, 0, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -297,9 +369,12 @@
         pointerCoords[i].clear();
     }
 
-    status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-            MotionClassification::NONE, 0, 0, 0, 0, 0, 0,
-            pointerCount, pointerProperties, pointerCoords);
+    status = mPublisher->publishMotionEvent(1, 0, 0, 0, INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+                                            MotionClassification::NONE, 1 /* xScale */,
+                                            1 /* yScale */, 0, 0, 0, 0,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                            AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0,
+                                            pointerCount, pointerProperties, pointerCoords);
     ASSERT_EQ(BAD_VALUE, status)
             << "publisher publishMotionEvent should return BAD_VALUE";
 }
@@ -308,6 +383,7 @@
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+    ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
     ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
 }
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 6db18ab..d1cb527 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -40,6 +40,7 @@
     sp<IBinder> touchableRegionCropHandle = new BBinder();
     InputWindowInfo i;
     i.token = new BBinder();
+    i.id = 1;
     i.name = "Foobar";
     i.layoutParamsFlags = 7;
     i.layoutParamsType = 39;
@@ -57,7 +58,6 @@
     i.hasFocus = false;
     i.hasWallpaper = false;
     i.paused = false;
-    i.layer = 7;
     i.ownerPid = 19;
     i.ownerUid = 24;
     i.inputFeatures = 29;
@@ -72,6 +72,7 @@
     p.setDataPosition(0);
     InputWindowInfo i2 = InputWindowInfo::read(p);
     ASSERT_EQ(i.token, i2.token);
+    ASSERT_EQ(i.id, i2.id);
     ASSERT_EQ(i.name, i2.name);
     ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
     ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
@@ -89,7 +90,6 @@
     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);
diff --git a/libs/input/tests/LatencyStatistics_test.cpp b/libs/input/tests/LatencyStatistics_test.cpp
new file mode 100644
index 0000000..eb12d4e
--- /dev/null
+++ b/libs/input/tests/LatencyStatistics_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 <input/LatencyStatistics.h>
+#include <cmath>
+#include <limits>
+#include <thread>
+
+namespace android {
+namespace test {
+
+TEST(LatencyStatisticsTest, ResetStats) {
+    LatencyStatistics stats{5min};
+    stats.addValue(5.0);
+    stats.addValue(19.3);
+    stats.addValue(20);
+    stats.reset();
+
+    ASSERT_EQ(stats.getCount(), 0u);
+    ASSERT_EQ(std::isnan(stats.getStDev()), true);
+    ASSERT_EQ(std::isnan(stats.getMean()), true);
+}
+
+TEST(LatencyStatisticsTest, AddStatsValue) {
+    LatencyStatistics stats{5min};
+    stats.addValue(5.0);
+
+    ASSERT_EQ(stats.getMin(), 5.0);
+    ASSERT_EQ(stats.getMax(), 5.0);
+    ASSERT_EQ(stats.getCount(), 1u);
+    ASSERT_EQ(stats.getMean(), 5.0);
+    ASSERT_EQ(stats.getStDev(), 0.0);
+}
+
+TEST(LatencyStatisticsTest, AddMultipleStatsValue) {
+    LatencyStatistics stats{5min};
+    stats.addValue(4.0);
+    stats.addValue(6.0);
+    stats.addValue(8.0);
+    stats.addValue(10.0);
+
+    float stdev = stats.getStDev();
+
+    ASSERT_EQ(stats.getMin(), 4.0);
+    ASSERT_EQ(stats.getMax(), 10.0);
+    ASSERT_EQ(stats.getCount(), 4u);
+    ASSERT_EQ(stats.getMean(), 7.0);
+    ASSERT_EQ(stdev * stdev, 5.0);
+}
+
+TEST(LatencyStatisticsTest, ShouldReportStats) {
+    LatencyStatistics stats{0min};
+    stats.addValue(5.0);
+
+    std::this_thread::sleep_for(1us);
+
+    ASSERT_EQ(stats.shouldReport(), true);
+}
+
+} // namespace test
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 62023fb..dd127fc 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -39,36 +39,93 @@
   CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Key, source, 20);
   CHECK_OFFSET(InputMessage::Body::Key, displayId, 24);
-  CHECK_OFFSET(InputMessage::Body::Key, action, 28);
-  CHECK_OFFSET(InputMessage::Body::Key, flags, 32);
-  CHECK_OFFSET(InputMessage::Body::Key, keyCode, 36);
-  CHECK_OFFSET(InputMessage::Body::Key, scanCode, 40);
-  CHECK_OFFSET(InputMessage::Body::Key, metaState, 44);
-  CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 48);
-  CHECK_OFFSET(InputMessage::Body::Key, downTime, 56);
+  CHECK_OFFSET(InputMessage::Body::Key, hmac, 28);
+  CHECK_OFFSET(InputMessage::Body::Key, action, 60);
+  CHECK_OFFSET(InputMessage::Body::Key, flags, 64);
+  CHECK_OFFSET(InputMessage::Body::Key, keyCode, 68);
+  CHECK_OFFSET(InputMessage::Body::Key, scanCode, 72);
+  CHECK_OFFSET(InputMessage::Body::Key, metaState, 76);
+  CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80);
+  CHECK_OFFSET(InputMessage::Body::Key, downTime, 88);
 
   CHECK_OFFSET(InputMessage::Body::Motion, seq, 0);
   CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8);
   CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16);
   CHECK_OFFSET(InputMessage::Body::Motion, source, 20);
   CHECK_OFFSET(InputMessage::Body::Motion, displayId, 24);
-  CHECK_OFFSET(InputMessage::Body::Motion, action, 28);
-  CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 32);
-  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, 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);
-  CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72);
-  CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80);
-  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88);
+  CHECK_OFFSET(InputMessage::Body::Motion, hmac, 28);
+  CHECK_OFFSET(InputMessage::Body::Motion, action, 60);
+  CHECK_OFFSET(InputMessage::Body::Motion, actionButton, 64);
+  CHECK_OFFSET(InputMessage::Body::Motion, flags, 68);
+  CHECK_OFFSET(InputMessage::Body::Motion, metaState, 72);
+  CHECK_OFFSET(InputMessage::Body::Motion, buttonState, 76);
+  CHECK_OFFSET(InputMessage::Body::Motion, classification, 80);
+  CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84);
+  CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88);
+  CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96);
+  CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100);
+  CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104);
+  CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108);
+  CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112);
+  CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116);
+  CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120);
+  CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128);
+  CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136);
+
+  CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
+  CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+  CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
 
   CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
   CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
 }
 
+void TestHeaderSize() {
+    static_assert(sizeof(InputMessage::Header) == 8);
+}
+
+/**
+ * We cannot use the Body::size() method here because it is not static for
+ * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
+ */
+void TestBodySize() {
+    static_assert(sizeof(InputMessage::Body::Key) == 96);
+    static_assert(sizeof(InputMessage::Body::Motion) ==
+                  offsetof(InputMessage::Body::Motion, pointers) +
+                          sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
+    static_assert(sizeof(InputMessage::Body::Finished) == 8);
+    static_assert(sizeof(InputMessage::Body::Focus) == 8);
+}
+
+// --- VerifiedInputEvent ---
+// Ensure that VerifiedInputEvent, VerifiedKeyEvent, VerifiedMotionEvent are packed.
+// We will treat them as byte collections when signing them. There should not be any uninitialized
+// data in-between fields. Otherwise, the padded data will affect the hmac value and verifications
+// will fail.
+
+void TestVerifiedEventSize() {
+    // VerifiedInputEvent
+    constexpr size_t VERIFIED_INPUT_EVENT_SIZE = sizeof(VerifiedInputEvent::type) +
+            sizeof(VerifiedInputEvent::deviceId) + sizeof(VerifiedInputEvent::eventTimeNanos) +
+            sizeof(VerifiedInputEvent::source) + sizeof(VerifiedInputEvent::displayId);
+    static_assert(sizeof(VerifiedInputEvent) == VERIFIED_INPUT_EVENT_SIZE);
+
+    // VerifiedKeyEvent
+    constexpr size_t VERIFIED_KEY_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+            sizeof(VerifiedKeyEvent::action) + sizeof(VerifiedKeyEvent::downTimeNanos) +
+            sizeof(VerifiedKeyEvent::flags) + sizeof(VerifiedKeyEvent::keyCode) +
+            sizeof(VerifiedKeyEvent::scanCode) + sizeof(VerifiedKeyEvent::metaState) +
+            sizeof(VerifiedKeyEvent::repeatCount);
+    static_assert(sizeof(VerifiedKeyEvent) == VERIFIED_KEY_EVENT_SIZE);
+
+    // VerifiedMotionEvent
+    constexpr size_t VERIFIED_MOTION_EVENT_SIZE = VERIFIED_INPUT_EVENT_SIZE +
+            sizeof(VerifiedMotionEvent::rawX) + sizeof(VerifiedMotionEvent::rawY) +
+            sizeof(VerifiedMotionEvent::actionMasked) + sizeof(VerifiedMotionEvent::downTimeNanos) +
+            sizeof(VerifiedMotionEvent::flags) + sizeof(VerifiedMotionEvent::metaState) +
+            sizeof(VerifiedMotionEvent::buttonState);
+    static_assert(sizeof(VerifiedMotionEvent) == VERIFIED_MOTION_EVENT_SIZE);
+}
+
 } // namespace android
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 815424e..1ec9358 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <input/DisplayViewport.h>
 #include <input/TouchVideoFrame.h>
 
 namespace android {
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 368446f..731eb6a 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -176,12 +176,13 @@
         EXPECT_EQ(pointerIndex, pointerCount);
 
         MotionEvent event;
-        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
-                action, 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*/, entry.eventTime.count(), pointerCount, properties, coords);
+        event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC,
+                         action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE,
+                         AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, 1 /*xScale*/,
+                         1 /*yScale*/, 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/,
+                         0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                         AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/,
+                         entry.eventTime.count(), pointerCount, properties, coords);
 
         events.emplace_back(event);
     }
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
new file mode 100644
index 0000000..a59dbe5
--- /dev/null
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <input/Input.h>
+
+namespace android {
+
+static KeyEvent getKeyEventWithFlags(int32_t flags) {
+    KeyEvent event;
+    event.initialize(2 /*deviceId*/, AINPUT_SOURCE_GAMEPAD, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
+                     AKEY_EVENT_ACTION_DOWN, flags, AKEYCODE_BUTTON_X, 121 /*scanCode*/,
+                     AMETA_ALT_ON, 1 /*repeatCount*/, 1000 /*downTime*/, 2000 /*eventTime*/);
+    return event;
+}
+
+static MotionEvent getMotionEventWithFlags(int32_t flags) {
+    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();
+    }
+
+    event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags,
+                     AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/,
+                     MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/,
+                     5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/,
+                     540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount,
+                     pointerProperties, pointerCoords);
+    return event;
+}
+
+TEST(VerifiedKeyEventTest, ConvertKeyEventToVerifiedKeyEvent) {
+    KeyEvent event = getKeyEventWithFlags(0);
+    VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+
+    ASSERT_EQ(VerifiedInputEvent::Type::KEY, verified.type);
+
+    ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+    ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+    ASSERT_EQ(event.getSource(), verified.source);
+    ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+    ASSERT_EQ(event.getAction(), verified.action);
+    ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+    ASSERT_EQ(event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, verified.flags);
+    ASSERT_EQ(event.getKeyCode(), verified.keyCode);
+    ASSERT_EQ(event.getScanCode(), verified.scanCode);
+    ASSERT_EQ(event.getMetaState(), verified.metaState);
+    ASSERT_EQ(event.getRepeatCount(), verified.repeatCount);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventContainsOnlyVerifiedFlags) {
+    KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_FALLBACK);
+    VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+    ASSERT_EQ(AKEY_EVENT_FLAG_CANCELED, verified.flags);
+}
+
+TEST(VerifiedKeyEventTest, VerifiedKeyEventDoesNotContainUnverifiedFlags) {
+    KeyEvent event = getKeyEventWithFlags(AKEY_EVENT_FLAG_EDITOR_ACTION);
+    VerifiedKeyEvent verified = verifiedKeyEventFromKeyEvent(event);
+    ASSERT_EQ(0, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, ConvertMotionEventToVerifiedMotionEvent) {
+    MotionEvent event = getMotionEventWithFlags(0);
+    VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+
+    ASSERT_EQ(VerifiedInputEvent::Type::MOTION, verified.type);
+
+    ASSERT_EQ(event.getDeviceId(), verified.deviceId);
+    ASSERT_EQ(event.getEventTime(), verified.eventTimeNanos);
+    ASSERT_EQ(event.getSource(), verified.source);
+    ASSERT_EQ(event.getDisplayId(), verified.displayId);
+
+    ASSERT_EQ(event.getRawX(0), verified.rawX);
+    ASSERT_EQ(event.getRawY(0), verified.rawY);
+    ASSERT_EQ(event.getAction(), verified.actionMasked);
+    ASSERT_EQ(event.getDownTime(), verified.downTimeNanos);
+    ASSERT_EQ(event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, verified.flags);
+    ASSERT_EQ(event.getMetaState(), verified.metaState);
+    ASSERT_EQ(event.getButtonState(), verified.buttonState);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventContainsOnlyVerifiedFlags) {
+    MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+                                                AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE);
+    VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+    ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, verified.flags);
+}
+
+TEST(VerifiedMotionEventTest, VerifiedMotionEventDoesNotContainUnverifiedFlags) {
+    MotionEvent event = getMotionEventWithFlags(AMOTION_EVENT_FLAG_TAINTED);
+    VerifiedMotionEvent verified = verifiedMotionEventFromMotionEvent(event);
+    ASSERT_EQ(0, verified.flags);
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
new file mode 100644
index 0000000..0f7e2fb
--- /dev/null
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -0,0 +1,338 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Choreographer"
+//#define LOG_NDEBUG 0
+
+#include <apex/choreographer.h>
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <utils/Looper.h>
+#include <utils/Mutex.h>
+#include <utils/Timers.h>
+
+#include <cinttypes>
+#include <optional>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+static inline const char* toString(bool value) {
+    return value ? "true" : "false";
+}
+
+struct FrameCallback {
+    AChoreographer_frameCallback callback;
+    AChoreographer_frameCallback64 callback64;
+    void* data;
+    nsecs_t dueTime;
+
+    inline bool operator<(const FrameCallback& rhs) const {
+        // Note that this is intentionally flipped because we want callbacks due sooner to be at
+        // the head of the queue
+        return dueTime > rhs.dueTime;
+    }
+};
+
+struct RefreshRateCallback {
+    AChoreographer_refreshRateCallback callback;
+    void* data;
+};
+
+class Choreographer : public DisplayEventDispatcher, public MessageHandler {
+public:
+    explicit Choreographer(const sp<Looper>& looper);
+    void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
+                                  AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay);
+    void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+    void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
+
+    enum {
+        MSG_SCHEDULE_CALLBACKS = 0,
+        MSG_SCHEDULE_VSYNC = 1
+    };
+    virtual void handleMessage(const Message& message) override;
+
+    static Choreographer* getForThread();
+    virtual ~Choreographer() = default;
+
+private:
+    Choreographer(const Choreographer&) = delete;
+
+    void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
+    void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+    void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId,
+                               nsecs_t vsyncPeriod) override;
+
+    void scheduleCallbacks();
+
+    // Protected by mLock
+    std::priority_queue<FrameCallback> mFrameCallbacks;
+
+    // Protected by mLock
+    std::vector<RefreshRateCallback> mRefreshRateCallbacks;
+    nsecs_t mVsyncPeriod = 0;
+
+    mutable Mutex mLock;
+
+    const sp<Looper> mLooper;
+    const std::thread::id mThreadId;
+    const std::optional<PhysicalDisplayId> mInternalDisplayId;
+};
+
+
+static thread_local Choreographer* gChoreographer;
+Choreographer* Choreographer::getForThread() {
+    if (gChoreographer == nullptr) {
+        sp<Looper> looper = Looper::getForThread();
+        if (!looper.get()) {
+            ALOGW("No looper prepared for thread");
+            return nullptr;
+        }
+        gChoreographer = new Choreographer(looper);
+        status_t result = gChoreographer->initialize();
+        if (result != OK) {
+            ALOGW("Failed to initialize");
+            return nullptr;
+        }
+    }
+    return gChoreographer;
+}
+
+Choreographer::Choreographer(const sp<Looper>& looper)
+      : DisplayEventDispatcher(looper),
+        mLooper(looper),
+        mThreadId(std::this_thread::get_id()),
+        mInternalDisplayId(SurfaceComposerClient::getInternalDisplayId()) {}
+
+void Choreographer::postFrameCallbackDelayed(
+        AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    FrameCallback callback{cb, cb64, data, now + delay};
+    {
+        AutoMutex _l{mLock};
+        mFrameCallbacks.push(callback);
+    }
+    if (callback.dueTime <= now) {
+        if (std::this_thread::get_id() != mThreadId) {
+            if (mLooper != nullptr) {
+                Message m{MSG_SCHEDULE_VSYNC};
+                mLooper->sendMessage(this, m);
+            } else {
+                scheduleVsync();
+            }
+        } else {
+            scheduleVsync();
+        }
+    } else {
+        if (mLooper != nullptr) {
+            Message m{MSG_SCHEDULE_CALLBACKS};
+            mLooper->sendMessageDelayed(delay, this, m);
+        } else {
+            scheduleCallbacks();
+        }
+    }
+}
+
+void Choreographer::registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data) {
+    {
+        AutoMutex _l{mLock};
+        for (const auto& callback : mRefreshRateCallbacks) {
+            // Don't re-add callbacks.
+            if (cb == callback.callback && data == callback.data) {
+                return;
+            }
+        }
+        mRefreshRateCallbacks.emplace_back(RefreshRateCallback{cb, data});
+        toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedDispatch);
+    }
+}
+
+void Choreographer::unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb,
+                                                  void* data) {
+    {
+        AutoMutex _l{mLock};
+        mRefreshRateCallbacks.erase(std::remove_if(mRefreshRateCallbacks.begin(),
+                                                   mRefreshRateCallbacks.end(),
+                                                   [&](const RefreshRateCallback& callback) {
+                                                       return cb == callback.callback &&
+                                                               data == callback.data;
+                                                   }),
+                                    mRefreshRateCallbacks.end());
+        if (mRefreshRateCallbacks.empty()) {
+            toggleConfigEvents(ISurfaceComposer::ConfigChanged::eConfigChangedSuppress);
+            // If callbacks are empty then clear out the most recently seen
+            // vsync period so that when another callback is registered then the
+            // up-to-date refresh rate can be communicated to the app again.
+            mVsyncPeriod = 0;
+        }
+    }
+}
+
+void Choreographer::scheduleCallbacks() {
+    AutoMutex _{mLock};
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    if (mFrameCallbacks.top().dueTime <= now) {
+        ALOGV("choreographer %p ~ scheduling vsync", this);
+        scheduleVsync();
+        return;
+    }
+}
+
+// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the
+// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for
+// the internal display implicitly.
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) {
+    std::vector<FrameCallback> callbacks{};
+    {
+        AutoMutex _l{mLock};
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+            callbacks.push_back(mFrameCallbacks.top());
+            mFrameCallbacks.pop();
+        }
+    }
+    for (const auto& cb : callbacks) {
+        if (cb.callback64 != nullptr) {
+            cb.callback64(timestamp, cb.data);
+        } else if (cb.callback != nullptr) {
+            cb.callback(timestamp, cb.data);
+        }
+    }
+}
+
+void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
+    ALOGV("choreographer %p ~ received hotplug event (displayId=%"
+            ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.",
+            this, displayId, toString(connected));
+}
+
+// TODO(b/74619554): The PhysicalDisplayId is ignored because currently
+// Choreographer only supports dispatching VSYNC events for the internal
+// display, so as such Choreographer does not support the notion of multiple
+// displays. When multi-display choreographer is properly supported, then
+// PhysicalDisplayId should no longer be ignored.
+void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId, int32_t,
+                                          nsecs_t vsyncPeriod) {
+    {
+        AutoMutex _l{mLock};
+        for (const auto& cb : mRefreshRateCallbacks) {
+            // Only perform the callback when the old refresh rate is different
+            // from the new refresh rate, so that we don't dispatch the callback
+            // on every single configuration change.
+            if (mVsyncPeriod != vsyncPeriod) {
+                cb.callback(vsyncPeriod, cb.data);
+            }
+        }
+        mVsyncPeriod = vsyncPeriod;
+    }
+}
+
+void Choreographer::handleMessage(const Message& message) {
+    switch (message.what) {
+    case MSG_SCHEDULE_CALLBACKS:
+        scheduleCallbacks();
+        break;
+    case MSG_SCHEDULE_VSYNC:
+        scheduleVsync();
+        break;
+    }
+}
+
+}
+
+/* Glue for the NDK interface */
+
+using namespace android;
+
+static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) {
+    return reinterpret_cast<Choreographer*>(choreographer);
+}
+
+static inline const Choreographer* AChoreographer_to_Choreographer(
+        const AChoreographer* choreographer) {
+    return reinterpret_cast<const Choreographer*>(choreographer);
+}
+
+static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) {
+    return reinterpret_cast<AChoreographer*>(choreographer);
+}
+
+AChoreographer* AChoreographer_getInstance() {
+    return Choreographer_to_AChoreographer(Choreographer::getForThread());
+}
+
+void AChoreographer_postFrameCallback(AChoreographer* choreographer,
+        AChoreographer_frameCallback callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            callback, nullptr, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
+        AChoreographer_frameCallback callback, void* data, long delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            callback, nullptr, data, ms2ns(delayMillis));
+}
+void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
+        AChoreographer_frameCallback64 callback, void* data) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            nullptr, callback, data, 0);
+}
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+        AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) {
+    AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed(
+            nullptr, callback, data, ms2ns(delayMillis));
+}
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+                                                AChoreographer_refreshRateCallback callback,
+                                                void* data) {
+    AChoreographer_to_Choreographer(choreographer)->registerRefreshRateCallback(callback, data);
+}
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                  AChoreographer_refreshRateCallback callback,
+                                                  void* data) {
+    AChoreographer_to_Choreographer(choreographer)->unregisterRefreshRateCallback(callback, data);
+}
+
+AChoreographer* AChoreographer_create() {
+    Choreographer* choreographer = new Choreographer(nullptr);
+    status_t result = choreographer->initialize();
+    if (result != OK) {
+        ALOGW("Failed to initialize");
+        return nullptr;
+    }
+    return Choreographer_to_AChoreographer(choreographer);
+}
+
+void AChoreographer_destroy(AChoreographer* choreographer) {
+    if (choreographer == nullptr) {
+        return;
+    }
+
+    delete AChoreographer_to_Choreographer(choreographer);
+}
+
+int AChoreographer_getFd(const AChoreographer* choreographer) {
+    return AChoreographer_to_Choreographer(choreographer)->getFd();
+}
+
+void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data) {
+    // Pass dummy fd and events args to handleEvent, since the underlying
+    // DisplayEventDispatcher doesn't need them outside of validating that a
+    // Looper instance didn't break, but these args circumvent those checks.
+    AChoreographer_to_Choreographer(choreographer)->handleEvent(-1, Looper::EVENT_INPUT, data);
+}
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
new file mode 100644
index 0000000..277635c
--- /dev/null
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -0,0 +1,307 @@
+/*
+ * 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 <apex/display.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayInfo.h>
+#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
+
+#include <algorithm>
+#include <optional>
+#include <type_traits>
+#include <vector>
+
+namespace android::display::impl {
+
+/**
+ * Implementation of ADisplayConfig
+ */
+struct DisplayConfigImpl {
+    /**
+     * The width in pixels of the display configuration.
+     */
+    int32_t width{0};
+
+    /**
+     * The height in pixels of the display configuration.
+     */
+
+    int32_t height{0};
+
+    /**
+     * The display density.
+     */
+    float density{0};
+
+    /**
+     * The refresh rate of the display configuration, in frames per second.
+     */
+    float fps{0.0};
+
+    /**
+     * The vsync offset at which surfaceflinger runs, in nanoseconds.
+     */
+    int64_t sfOffset{0};
+
+    /**
+     * The vsync offset at which applications run, in nanoseconds.
+     */
+    int64_t appOffset{0};
+};
+
+// DisplayConfigImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value);
+
+/**
+ * Implementation of ADisplay
+ */
+struct DisplayImpl {
+    /**
+     * A physical display ID, unique to this display.
+     */
+    PhysicalDisplayId id;
+
+    /**
+     * The type of the display, i.e. whether it is an internal or external
+     * display.
+     */
+    ADisplayType type;
+
+    /**
+     * The preferred WCG dataspace
+     */
+    ADataSpace wcgDataspace;
+
+    /**
+     * The preferred WCG pixel format
+     */
+    AHardwareBuffer_Format wcgPixelFormat;
+
+    /**
+     * Number of supported configs
+     */
+    size_t numConfigs;
+
+    /**
+     * Set of supported configs by this display.
+     */
+    DisplayConfigImpl* configs;
+};
+
+// DisplayImpl allocation is not managed through C++ memory apis, so
+// preventing calling the destructor here.
+static_assert(std::is_trivially_destructible<DisplayImpl>::value);
+
+} // namespace android::display::impl
+
+using namespace android;
+using namespace android::display::impl;
+
+#define CHECK_NOT_NULL(name) \
+    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+namespace {
+
+sp<IBinder> getToken(ADisplay* display) {
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+    return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
+}
+
+} // namespace
+
+namespace android {
+
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
+    const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
+    const size_t size = ids.size();
+    if (size == 0) {
+        return NO_INIT;
+    }
+
+    std::vector<DisplayConfigImpl> configsPerDisplay[size];
+    int numConfigs = 0;
+    for (int i = 0; i < size; ++i) {
+        const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
+
+        DisplayInfo info;
+        if (const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info);
+            status != OK) {
+            return status;
+        }
+
+        Vector<DisplayConfig> configs;
+        if (const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
+            status != OK) {
+            return status;
+        }
+        if (configs.empty()) {
+            return NO_INIT;
+        }
+
+        numConfigs += configs.size();
+        configsPerDisplay[i].reserve(configs.size());
+        for (int j = 0; j < configs.size(); ++j) {
+            const DisplayConfig& config = configs[j];
+            configsPerDisplay[i].emplace_back(
+                    DisplayConfigImpl{config.resolution.getWidth(), config.resolution.getHeight(),
+                                      info.density, config.refreshRate, config.sfVsyncOffset,
+                                      config.appVsyncOffset});
+        }
+    }
+
+    const std::optional<PhysicalDisplayId> internalId =
+            SurfaceComposerClient::getInternalDisplayId();
+    ui::Dataspace defaultDataspace;
+    ui::PixelFormat defaultPixelFormat;
+    ui::Dataspace wcgDataspace;
+    ui::PixelFormat wcgPixelFormat;
+
+    const status_t status =
+            SurfaceComposerClient::getCompositionPreference(&defaultDataspace, &defaultPixelFormat,
+                                                            &wcgDataspace, &wcgPixelFormat);
+    if (status != NO_ERROR) {
+        return status;
+    }
+
+    // Here we allocate all our required memory in one block. The layout is as
+    // follows:
+    // ------------------------------------------------------------
+    // | DisplayImpl pointers | DisplayImpls | DisplayConfigImpls |
+    // ------------------------------------------------------------
+    //
+    // The caller will be given a DisplayImpl** which points to the beginning of
+    // the block of DisplayImpl pointers.
+    // Each DisplayImpl* points to a DisplayImpl in the second block.
+    // Each DisplayImpl contains a DisplayConfigImpl*, which points to a
+    // contiguous block of DisplayConfigImpls specific to that display.
+    DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
+            malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
+                   sizeof(DisplayConfigImpl) * numConfigs));
+    DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
+    DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);
+
+    for (size_t i = 0; i < size; ++i) {
+        const PhysicalDisplayId id = ids[i];
+        const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
+                                                     : ADisplayType::DISPLAY_TYPE_EXTERNAL;
+        const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
+        memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
+
+        displayData[i] = DisplayImpl{id,
+                                     type,
+                                     static_cast<ADataSpace>(wcgDataspace),
+                                     static_cast<AHardwareBuffer_Format>(wcgPixelFormat),
+                                     configs.size(),
+                                     configData};
+        impls[i] = displayData + i;
+        // Advance the configData pointer so that future configs are written to
+        // the correct display.
+        configData += configs.size();
+    }
+
+    *outDisplays = reinterpret_cast<ADisplay**>(impls);
+    return size;
+}
+
+void ADisplay_release(ADisplay** displays) {
+    if (displays == nullptr) {
+        return;
+    }
+    free(displays);
+}
+
+float ADisplay_getMaxSupportedFps(ADisplay* display) {
+    CHECK_NOT_NULL(display);
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+    float maxFps = 0.0;
+    for (int i = 0; i < impl->numConfigs; ++i) {
+        maxFps = std::max(maxFps, impl->configs[i].fps);
+    }
+    return maxFps;
+}
+
+ADisplayType ADisplay_getDisplayType(ADisplay* display) {
+    CHECK_NOT_NULL(display);
+
+    return reinterpret_cast<DisplayImpl*>(display)->type;
+}
+
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+                                          AHardwareBuffer_Format* outPixelFormat) {
+    CHECK_NOT_NULL(display);
+    CHECK_NOT_NULL(outDataspace);
+    CHECK_NOT_NULL(outPixelFormat);
+
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+    *outDataspace = impl->wcgDataspace;
+    *outPixelFormat = impl->wcgPixelFormat;
+}
+
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
+    CHECK_NOT_NULL(display);
+
+    sp<IBinder> token = getToken(display);
+    const int index = SurfaceComposerClient::getActiveConfig(token);
+    if (index < 0) {
+        return index;
+    }
+
+    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+
+    *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index);
+    return OK;
+}
+
+float ADisplayConfig_getDensity(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->density;
+}
+
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->width;
+}
+
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->height;
+}
+
+float ADisplayConfig_getFps(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->fps;
+}
+
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset;
+}
+
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) {
+    CHECK_NOT_NULL(config);
+
+    return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
new file mode 100644
index 0000000..c956578
--- /dev/null
+++ b/libs/nativedisplay/Android.bp
@@ -0,0 +1,70 @@
+// 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.
+
+ndk_headers {
+    name: "libnativedisplay_ndk_headers",
+    from: "include/android",
+    to: "android",
+    srcs: ["include/android/*.h"],
+    license: "NOTICE",
+}
+
+cc_library_headers {
+    name: "libnativedisplay_headers",
+    export_include_dirs: ["include"],
+}
+
+cc_library {
+    name: "libnativedisplay",
+    export_include_dirs: [
+        "include",
+    ],
+
+    clang: true,
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-enum-compare",
+        "-Wno-unused-function",
+    ],
+
+    version_script: "libnativedisplay.map.txt",
+
+    srcs: [
+        "AChoreographer.cpp",
+        "ADisplay.cpp",
+        "surfacetexture/surface_texture.cpp",
+        "surfacetexture/SurfaceTexture.cpp",
+        "surfacetexture/ImageConsumer.cpp",
+        "surfacetexture/EGLConsumer.cpp",
+    ],
+
+    shared_libs: [
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "libui",
+        "libutils",
+        "libcutils",
+        "libEGL",
+        "libGLESv2",
+        "libnativehelper",
+    ],
+
+    header_libs: [
+        "libnativedisplay_headers",
+    ],
+
+}
diff --git a/libs/nativedisplay/MODULE_LICENSE_APACHE2 b/libs/nativedisplay/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/nativedisplay/MODULE_LICENSE_APACHE2
diff --git a/libs/nativedisplay/NOTICE b/libs/nativedisplay/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/nativedisplay/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/include/android/choreographer.h b/libs/nativedisplay/include/android/choreographer.h
similarity index 62%
rename from include/android/choreographer.h
rename to libs/nativedisplay/include/android/choreographer.h
index 346861f..5fd3de9 100644
--- a/include/android/choreographer.h
+++ b/libs/nativedisplay/include/android/choreographer.h
@@ -54,6 +54,13 @@
  */
 typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
 
+/**
+ * Prototype of the function that is called when the display refresh rate
+ * changes. It's passed the new vsync period in nanoseconds, as well as the data
+ * pointer provided by the application that registered a callback.
+ */
+typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos, void* data);
+
 #if __ANDROID_API__ >= 24
 
 /**
@@ -87,7 +94,7 @@
  *
  * Available since API level 29.
  */
-void AChoreographer_postFrameCallback64(AChoreographer* chroreographer,
+void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
                 AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
 
 /**
@@ -102,6 +109,42 @@
 
 #endif /* __ANDROID_API__ >= 29 */
 
+#if __ANDROID_API__ >= 30
+
+/**
+ * Registers a callback to be run when the display refresh rate changes. The
+ * data pointer provided will be passed to the callback function when it's
+ * called. The same callback may be registered multiple times, provided that a
+ * different data pointer is provided each time.
+ *
+ * If an application registers a callback for this choreographer instance when
+ * no new callbacks were previously registered, that callback is guaranteed to
+ * be dispatched. However, if the callback and associated data pointer are
+ * unregistered prior to running the callback, then the callback may be silently
+ * dropped.
+ *
+ * This api is thread-safe. Any thread is allowed to register a new refresh
+ * rate callback for the choreographer instance.
+ */
+void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
+                                                AChoreographer_refreshRateCallback, void* data);
+
+/**
+ * Unregisters a callback to be run when the display refresh rate changes, along
+ * with the data pointer previously provided when registering the callback. The
+ * callback is only unregistered when the data pointer matches one that was
+ * previously registered.
+ *
+ * This api is thread-safe. Any thread is allowed to unregister an existing
+ * refresh rate callback for the choreographer instance. When a refresh rate
+ * callback and associated data pointer are unregistered, then there is a
+ * guarantee that when the unregistration completes that that callback will not
+ * be run with the data pointer passed.
+ */
+void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer,
+                                                  AChoreographer_refreshRateCallback, void* data);
+#endif /* __ANDROID_API__ >= 30 */
+
 __END_DECLS
 
 #endif // ANDROID_CHOREOGRAPHER_H
diff --git a/libs/nativedisplay/include/apex/choreographer.h b/libs/nativedisplay/include/apex/choreographer.h
new file mode 100644
index 0000000..683abc4
--- /dev/null
+++ b/libs/nativedisplay/include/apex/choreographer.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 <android/choreographer.h>
+#include <inttypes.h>
+
+__BEGIN_DECLS
+
+/**
+ * Creates an instance of AChoreographer.
+ *
+ * The key differences between this method and AChoreographer_getInstance are:
+ * 1. The returned AChoreographer instance is not a thread-local, and
+ * 2. This method does not require an existing ALooper attached to the thread.
+ */
+AChoreographer* AChoreographer_create();
+
+/**
+ * Destroys a choreographer instance created from AChoreographer_create.
+ */
+void AChoreographer_destroy(AChoreographer* choreographer);
+
+/**
+ * Returns the underlying file descriptor associated with this choreographer
+ * instance.
+ *
+ * The caller can listen to the file descriptor to respond to any AChoreographer
+ * events. One such way is registering the file descriptor to a Looper instance,
+ * although this is not a requirement.
+ */
+int AChoreographer_getFd(const AChoreographer* choreographer);
+
+/**
+ * Provides a callback to handle all pending events emitted by this
+ * choreographer instance. Specifically, this delegates to the callbacks
+ * previously registered to choreographer.
+ *
+ * If the associated file descriptor is attached to a Looper instance, then the
+ * callback attached to that Looper is expected to handle exceptional Looper
+ * events.
+ */
+void AChoreographer_handlePendingEvents(AChoreographer* choreographer, void* data);
+
+__END_DECLS
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
new file mode 100644
index 0000000..a7eaf87
--- /dev/null
+++ b/libs/nativedisplay/include/apex/display.h
@@ -0,0 +1,139 @@
+/*
+ * 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/data_space.h>
+#include <android/hardware_buffer.h>
+#include <inttypes.h>
+
+// TODO: the intention of these apis is to be stable - hence they are defined in
+// an apex directory. But because they don't yet need to be stable, hold off on
+// making them stable until a Mainline module needs them.
+// __BEGIN_DECLS
+
+namespace android {
+
+/**
+ * Opaque handle for a native display
+ */
+typedef struct ADisplay ADisplay;
+
+/**
+ * Enum describing the possible types of a display
+ */
+enum ADisplayType {
+    /**
+     * A display that is the internal, or "primary" display for a device.
+     */
+    DISPLAY_TYPE_INTERNAL = 0,
+
+    /**
+     * A display that is externally connected for a device.
+     */
+    DISPLAY_TYPE_EXTERNAL = 1,
+};
+
+/**
+ * Opaque handle for display metadata
+ */
+typedef struct ADisplayConfig ADisplayConfig;
+
+/**
+ * Acquires a list of display handles. Memory is allocated for the list and is
+ * owned by the caller. The caller is responsible for freeing this memory by
+ * calling ADisplayList_release.
+ *
+ * Returns the size of the returned list on success.
+ * Returns -errno on error.
+ */
+int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays);
+
+/**
+ * Releases a list of display handles created by
+ * ADisplayList_acquirePhysicalDisplays.
+ */
+void ADisplay_release(ADisplay** displays);
+
+/**
+ * Queries the maximum supported fps for the given display.
+ */
+float ADisplay_getMaxSupportedFps(ADisplay* display);
+
+/**
+ * Queries the display's type.
+ */
+ADisplayType ADisplay_getDisplayType(ADisplay* display);
+
+/**
+ * Queries the display's preferred WCG format
+ */
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+                                          AHardwareBuffer_Format* outPixelFormat);
+
+/**
+ * Gets the current display configuration for the given display.
+ *
+ * Memory is *not* allocated for the caller. As such, the returned output
+ * configuration's lifetime will not be longer than the ADisplay* passed to this
+ * function - if ADisplay_release is called destroying the ADisplay object then
+ * it is invalid to access the ADisplayConfig returned here.
+ *
+ * Note that the current display configuration can change. Listening to updates
+ * to the current display configuration should be done via Choreographer. If
+ * such an update is observed, then this method should be recalled to get the
+ * new current configuration.
+ *
+ * Returns OK on success, -errno on failure.
+ */
+int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);
+
+/**
+ * Queries the density for a given display configuration.
+ */
+float ADisplayConfig_getDensity(ADisplayConfig* config);
+
+/**
+ * Queries the width in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getWidth(ADisplayConfig* config);
+
+/**
+ * Queries the height in pixels for a given display configuration.
+ */
+int32_t ADisplayConfig_getHeight(ADisplayConfig* config);
+
+/**
+ * Queries the display refresh rate for a given display configuration.
+ */
+float ADisplayConfig_getFps(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which the system compositor is scheduled to
+ * run. If a vsync occurs at time T, and the compositor runs at time T + S, then
+ * this returns S in nanoseconds.
+ */
+int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config);
+
+/**
+ * Queries the vsync offset from which applications are scheduled to run. If a
+ * vsync occurs at time T, and applications run at time T + S, then this returns
+ * S in nanoseconds.
+ */
+int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config);
+
+} // namespace android
+// __END_DECLS
diff --git a/libs/nativedisplay/include/surfacetexture/EGLConsumer.h b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
new file mode 100644
index 0000000..444722b
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/EGLConsumer.h
@@ -0,0 +1,309 @@
+/*
+ * 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <gui/BufferQueueDefs.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class SurfaceTexture;
+
+/*
+ * EGLConsumer implements the parts of SurfaceTexture that deal with
+ * textures attached to an GL context.
+ */
+class EGLConsumer {
+public:
+    EGLConsumer();
+
+    /**
+     * updateTexImage acquires the most recently queued buffer, and sets the
+     * image contents of the target texture to it.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     *
+     * This calls doGLFenceWait to ensure proper synchronization.
+     */
+    status_t updateTexImage(SurfaceTexture& st);
+
+    /*
+     * releaseTexImage releases the texture acquired in updateTexImage().
+     * This is intended to be used in single buffer mode.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     */
+    status_t releaseTexImage(SurfaceTexture& st);
+
+    /**
+     * detachFromContext detaches the EGLConsumer from the calling thread's
+     * current OpenGL ES context.  This context must be the same as the context
+     * that was current for previous calls to updateTexImage.
+     *
+     * Detaching a EGLConsumer from an OpenGL ES context will result in the
+     * deletion of the OpenGL ES texture object into which the images were being
+     * streamed.  After a EGLConsumer has been detached from the OpenGL ES
+     * context calls to updateTexImage will fail returning INVALID_OPERATION
+     * until the EGLConsumer is attached to a new OpenGL ES context using the
+     * attachToContext method.
+     */
+    status_t detachFromContext(SurfaceTexture& st);
+
+    /**
+     * attachToContext attaches a EGLConsumer that is currently in the
+     * 'detached' state to the current OpenGL ES context.  A EGLConsumer is
+     * in the 'detached' state iff detachFromContext has successfully been
+     * called and no calls to attachToContext have succeeded since the last
+     * detachFromContext call.  Calls to attachToContext made on a
+     * EGLConsumer that is not in the 'detached' state will result in an
+     * INVALID_OPERATION error.
+     *
+     * The tex argument specifies the OpenGL ES texture object name in the
+     * new context into which the image contents will be streamed.  A successful
+     * call to attachToContext will result in this texture object being bound to
+     * the texture target and populated with the image contents that were
+     * current at the time of the last call to detachFromContext.
+     */
+    status_t attachToContext(uint32_t tex, SurfaceTexture& st);
+
+    /**
+     * onAcquireBufferLocked amends the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase behavior.
+     */
+    void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st);
+
+    /**
+     * onReleaseBufferLocked amends the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase.
+     */
+    void onReleaseBufferLocked(int slot);
+
+    /**
+     * onFreeBufferLocked 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 EGLImage in that slot.  Otherwise it has no effect.
+     */
+    void onFreeBufferLocked(int slotIndex);
+
+    /**
+     * onAbandonLocked amends the ConsumerBase method to clear
+     * mCurrentTextureImage in addition to the ConsumerBase behavior.
+     */
+    void onAbandonLocked();
+
+protected:
+    struct PendingRelease {
+        PendingRelease()
+              : isPending(false),
+                currentTexture(-1),
+                graphicBuffer(),
+                display(nullptr),
+                fence(nullptr) {}
+
+        bool isPending;
+        int currentTexture;
+        sp<GraphicBuffer> graphicBuffer;
+        EGLDisplay display;
+        EGLSyncKHR fence;
+    };
+
+    /**
+     * This releases the buffer in the slot referenced by mCurrentTexture,
+     * then updates state to refer to the BufferItem, which must be a
+     * newly-acquired buffer. If pendingRelease is not null, the parameters
+     * which would have been passed to releaseBufferLocked upon the successful
+     * 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,
+                                    SurfaceTexture& st);
+
+    /**
+     * Binds mTexName and the current buffer to mTexTarget.  Uses
+     * mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
+     * bind succeeds, this calls doGLFenceWait.
+     */
+    status_t bindTextureImageLocked(SurfaceTexture& st);
+
+    /**
+     * Gets the current EGLDisplay and EGLContext values, and compares them
+     * to mEglDisplay and mEglContext.  If the fields have been previously
+     * set, the values must match; if not, the fields are set to the current
+     * values.
+     * The contextCheck argument is used to ensure that a GL context is
+     * properly set; when set to false, the check is not performed.
+     */
+    status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false);
+
+    /**
+     * EglImage is a utility class for tracking and creating EGLImageKHRs. There
+     * is primarily just one image per slot, but there is also special cases:
+     *  - For releaseTexImage, we use a debug image (mReleasedTexImage)
+     *  - After freeBuffer, we must still keep the current image/buffer
+     * Reference counting EGLImages lets us handle all these cases easily while
+     * also only creating new EGLImages from buffers when required.
+     */
+    class EglImage : public LightRefBase<EglImage> {
+    public:
+        EglImage(sp<GraphicBuffer> graphicBuffer);
+
+        /**
+         * 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, bool forceCreate = false);
+
+        /**
+         * This calls glEGLImageTargetTexture2DOES to bind the image to the
+         * texture in the specified texture target.
+         */
+        void bindToTextureTarget(uint32_t texTarget);
+
+        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+        const native_handle* graphicBufferHandle() {
+            return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
+        }
+
+    private:
+        // Only allow instantiation using ref counting.
+        friend class LightRefBase<EglImage>;
+        virtual ~EglImage();
+
+        // createImage creates a new EGLImage from a GraphicBuffer.
+        EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer);
+
+        // Disallow copying
+        EglImage(const EglImage& rhs);
+        void operator=(const EglImage& rhs);
+
+        // mGraphicBuffer is the buffer that was used to create this image.
+        sp<GraphicBuffer> mGraphicBuffer;
+
+        // mEglImage is the EGLImage created from mGraphicBuffer.
+        EGLImageKHR mEglImage;
+
+        // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+        EGLDisplay mEglDisplay;
+
+        // mCropRect is the crop rectangle passed to EGL when mEglImage
+        // was created.
+        Rect mCropRect;
+    };
+
+    /**
+     * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
+     * stream to ensure that it is safe for future OpenGL ES commands to
+     * access the current texture buffer.
+     */
+    status_t doGLFenceWaitLocked(SurfaceTexture& st) const;
+
+    /**
+     * syncForReleaseLocked performs the synchronization needed to release the
+     * current slot from an OpenGL ES context.  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(EGLDisplay dpy, SurfaceTexture& st);
+
+    /**
+     * returns a graphic buffer used when the texture image has been released
+     */
+    static sp<GraphicBuffer> getDebugTexImageBuffer();
+
+    /**
+     * The default consumer usage flags that EGLConsumer always sets on its
+     * BufferQueue instance; these will be OR:d with any additional flags passed
+     * from the EGLConsumer user. In particular, EGLConsumer will always
+     * consume buffers as hardware textures.
+     */
+    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+    /**
+     * mCurrentTextureImage is the EglImage/buffer of 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<EglImage> mCurrentTextureImage;
+
+    /**
+     * EGLSlot contains the information and object references that
+     * EGLConsumer maintains about a BufferQueue buffer slot.
+     */
+    struct EglSlot {
+        EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+        /**
+         * mEglImage is the EGLImage created from mGraphicBuffer.
+         */
+        sp<EglImage> mEglImage;
+
+        /**
+         * mFence is the EGL sync object that must signal before the buffer
+         * associated with this buffer slot may be dequeued. It is initialized
+         * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
+         * on a compile-time option) set to a new sync object in updateTexImage.
+         */
+        EGLSyncKHR mEglFence;
+    };
+
+    /**
+     * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently
+     * associated.  It is intialized to EGL_NO_DISPLAY and gets set to the
+     * current display when updateTexImage is called for the first time and when
+     * attachToContext is called.
+     */
+    EGLDisplay mEglDisplay;
+
+    /**
+     * mEglContext is the OpenGL ES context with which this EGLConsumer is
+     * currently associated.  It is initialized to EGL_NO_CONTEXT and gets set
+     * to the current GL context when updateTexImage is called for the first
+     * time and when attachToContext is called.
+     */
+    EGLContext mEglContext;
+
+    /**
+     * mEGLSlots 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.
+     */
+    EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+    /**
+     * protects static initialization
+     */
+    static Mutex sStaticInitLock;
+
+    /**
+     * mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+     * mode and releaseTexImage() has been called
+     */
+    static sp<GraphicBuffer> sReleasedTexImageBuffer;
+    sp<EglImage> mReleasedTexImage;
+};
+
+} // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
new file mode 100644
index 0000000..35ae3d2
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
@@ -0,0 +1,87 @@
+/*
+ * 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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueueDefs.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+
+namespace android {
+
+class SurfaceTexture;
+class DequeueBufferCallbacks;
+
+/*
+ * ImageConsumer implements the parts of SurfaceTexture that deal with
+ * images consumed by HWUI view system.
+ */
+class ImageConsumer {
+public:
+    typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+                                                          EGLDisplay* display, int* releaseFence,
+                                                          void* fencePassThroughHandle);
+
+    typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+    sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                    bool* outQueueEmpty, SurfaceTexture& cb,
+                                    SurfaceTexture_createReleaseFence createFence,
+                                    SurfaceTexture_fenceWait fenceWait,
+                                    void* fencePassThroughHandle);
+
+    /**
+     * onReleaseBufferLocked amends the ConsumerBase method to update the
+     * mImageSlots array in addition to the ConsumerBase.
+     */
+    void onReleaseBufferLocked(int slot);
+
+private:
+    /**
+     * ImageSlot contains the information and object references that
+     * ImageConsumer maintains about a BufferQueue buffer slot.
+     */
+    class ImageSlot {
+    public:
+        ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+        inline EGLSyncKHR& eglFence() { return mEglFence; }
+
+    private:
+        /**
+         * mEglFence is the EGL sync object that must signal before the buffer
+         * associated with this buffer slot may be dequeued.
+         */
+        EGLSyncKHR mEglFence;
+    };
+
+    /**
+     * ImageConsumer stores the SkImages 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.
+     */
+    ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+} /* namespace android */
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
new file mode 100644
index 0000000..6eaa84e
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -0,0 +1,472 @@
+/*
+ * 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/hardware_buffer.h>
+#include <gui/BufferQueueDefs.h>
+#include <gui/ConsumerBase.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <sys/cdefs.h>
+#include <system/graphics.h>
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+#include "EGLConsumer.h"
+#include "ImageConsumer.h"
+
+namespace android {
+
+/*
+ * SurfaceTexture consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to HWUI render thread as a SkImage and to
+ * an application GL render thread as an OpenGL texture.
+ *
+ * When attached to an application GL render thread, a typical usage
+ * pattern is to set up the SurfaceTexture with the
+ * desired options, and call updateTexImage() when a new frame is desired.
+ * If a new frame is available, the texture will be updated.  If not,
+ * the previous contents are retained.
+ *
+ * When attached to a HWUI render thread, the TextureView implementation
+ * calls dequeueBuffer, which either pulls a new buffer or returns the
+ * last cached buffer if BufferQueue is empty.
+ * When attached to HWUI render thread, SurfaceTexture is compatible to
+ * both Vulkan and GL drawing pipelines.
+ */
+class ANDROID_API SurfaceTexture : public ConsumerBase {
+public:
+    /**
+     * Callback function needed by dequeueBuffer. It creates a fence,
+     * that is signalled, when the previous buffer is no longer in use by HWUI
+     * and can be used by written by the producer.
+     */
+    typedef status_t (*SurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+                                                          EGLDisplay* display, int* releaseFence,
+                                                          void* passThroughHandle);
+
+    /**
+     * Callback function needed by dequeueBuffer. It waits for the new buffer
+     * fence to signal, before issuing any draw commands.
+     */
+    typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* passThroughHandle);
+
+    enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES
+    typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
+
+    /**
+     * SurfaceTexture constructs a new SurfaceTexture object. If the constructor
+     * with the tex parameter is used, tex indicates the name of the OpenGL ES
+     * texture to which images are to be streamed. texTarget specifies the
+     * OpenGL ES texture target to which the texture will be bound in
+     * updateTexImage. useFenceSync specifies whether fences should be used to
+     * synchronize access to buffers if that behavior is enabled at
+     * compile-time.
+     *
+     * A SurfaceTexture may be detached from one OpenGL ES context and then
+     * attached to a different context using the detachFromContext and
+     * attachToContext methods, respectively. The intention of these methods is
+     * purely to allow a SurfaceTexture to be transferred from one consumer
+     * context to another. If such a transfer is not needed there is no
+     * requirement that either of these methods be called.
+     *
+     * If the constructor with the tex parameter is used, the SurfaceTexture is
+     * created in a state where it is considered attached to an OpenGL ES
+     * context for the purposes of the attachToContext and detachFromContext
+     * methods. However, despite being considered "attached" to a context, the
+     * specific OpenGL ES context doesn't get latched until the first call to
+     * updateTexImage. After that point, all calls to updateTexImage must be
+     * made with the same OpenGL ES context current.
+     *
+     * If the constructor without the tex parameter is used, the SurfaceTexture
+     * is created in a detached state, and attachToContext must be called before
+     * calls to updateTexImage.
+     */
+    SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t textureTarget,
+                   bool useFenceSync, bool isControlledByApp);
+
+    SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t textureTarget, bool useFenceSync,
+                   bool isControlledByApp);
+
+    /**
+     * updateTexImage acquires the most recently queued buffer, and sets the
+     * image contents of the target texture to it.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     *
+     * This calls doGLFenceWait to ensure proper synchronization.
+     */
+    status_t updateTexImage();
+
+    /**
+     * releaseTexImage releases the texture acquired in updateTexImage().
+     * This is intended to be used in single buffer mode.
+     *
+     * This call may only be made while the OpenGL ES context to which the
+     * target texture belongs is bound to the calling thread.
+     */
+    status_t releaseTexImage();
+
+    /**
+     * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
+     * associated with the texture image set by the most recent call to
+     * updateTexImage.
+     *
+     * This transform matrix maps 2D homogeneous texture coordinates of the form
+     * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture
+     * coordinate that should be used to sample that location from the texture.
+     * Sampling the texture outside of the range of this transform is undefined.
+     *
+     * This transform is necessary to compensate for transforms that the stream
+     * content producer may implicitly apply to the content. By forcing users of
+     * a SurfaceTexture to apply this transform we avoid performing an extra
+     * copy of the data that would be needed to hide the transform from the
+     * user.
+     *
+     * The matrix is stored in column-major order so that it may be passed
+     * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv
+     * functions.
+     */
+    void getTransformMatrix(float mtx[16]);
+
+    /**
+     * Computes the transform matrix documented by getTransformMatrix
+     * from the BufferItem sub parts.
+     */
+    static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+                                       const Rect& cropRect, uint32_t transform, bool filtering);
+
+    /**
+     * 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);
+
+    /**
+     * getTimestamp retrieves the timestamp associated with the texture image
+     * set by the most recent call to updateTexImage.
+     *
+     * The timestamp is in nanoseconds, and is monotonically increasing. Its
+     * other semantics (zero point, etc) are source-dependent and should be
+     * documented by the source.
+     */
+    int64_t getTimestamp();
+
+    /**
+     * getDataSpace retrieves the DataSpace associated with the texture image
+     * set by the most recent call to updateTexImage.
+     */
+    android_dataspace getCurrentDataSpace();
+
+    /**
+     * getFrameNumber retrieves the frame number associated with the texture
+     * image set by the most recent call to updateTexImage.
+     *
+     * The frame number is an incrementing counter set to 0 at the creation of
+     * the BufferQueue associated with this consumer.
+     */
+    uint64_t getFrameNumber();
+
+    /**
+     * setDefaultBufferSize is used to set the size of buffers returned by
+     * requestBuffers when a with and height of zero is requested.
+     * A call to setDefaultBufferSize() may trigger requestBuffers() to
+     * be called from the client.
+     * The width and height parameters must be no greater than the minimum of
+     * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+     * An error due to invalid dimensions might not be reported until
+     * updateTexImage() is called.
+     */
+    status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+    /**
+     * setFilteringEnabled sets whether the transform matrix should be computed
+     * for use with bilinear filtering.
+     */
+    void setFilteringEnabled(bool enabled);
+
+    /**
+     * getCurrentTextureTarget returns the texture target of the current
+     * texture as returned by updateTexImage().
+     */
+    uint32_t getCurrentTextureTarget() const;
+
+    /**
+     * getCurrentCrop returns the cropping rectangle of the current buffer.
+     */
+    Rect getCurrentCrop() const;
+
+    /**
+     * getCurrentTransform returns the transform of the current buffer.
+     */
+    uint32_t getCurrentTransform() const;
+
+    /**
+     * getCurrentScalingMode returns the scaling mode of the current buffer.
+     */
+    uint32_t getCurrentScalingMode() const;
+
+    /**
+     * getCurrentFence returns the fence indicating when the current buffer is
+     * ready to be read from.
+     */
+    sp<Fence> getCurrentFence() const;
+
+    /**
+     * getCurrentFence returns the FenceTime indicating when the current
+     * buffer is ready to be read from.
+     */
+    std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
+    /**
+     * setConsumerUsageBits overrides the ConsumerBase method to OR
+     * DEFAULT_USAGE_FLAGS to usage.
+     */
+    status_t setConsumerUsageBits(uint64_t usage);
+
+    /**
+     * detachFromContext detaches the SurfaceTexture from the calling thread's
+     * current OpenGL ES context.  This context must be the same as the context
+     * that was current for previous calls to updateTexImage.
+     *
+     * Detaching a SurfaceTexture from an OpenGL ES context will result in the
+     * deletion of the OpenGL ES texture object into which the images were being
+     * streamed.  After a SurfaceTexture has been detached from the OpenGL ES
+     * context calls to updateTexImage will fail returning INVALID_OPERATION
+     * until the SurfaceTexture is attached to a new OpenGL ES context using the
+     * attachToContext method.
+     */
+    status_t detachFromContext();
+
+    /**
+     * attachToContext attaches a SurfaceTexture that is currently in the
+     * 'detached' state to the current OpenGL ES context.  A SurfaceTexture is
+     * in the 'detached' state iff detachFromContext has successfully been
+     * called and no calls to attachToContext have succeeded since the last
+     * detachFromContext call.  Calls to attachToContext made on a
+     * SurfaceTexture that is not in the 'detached' state will result in an
+     * INVALID_OPERATION error.
+     *
+     * The tex argument specifies the OpenGL ES texture object name in the
+     * new context into which the image contents will be streamed.  A successful
+     * call to attachToContext will result in this texture object being bound to
+     * the texture target and populated with the image contents that were
+     * current at the time of the last call to detachFromContext.
+     */
+    status_t attachToContext(uint32_t tex);
+
+    sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                    float* outTransformMatrix, bool* outQueueEmpty,
+                                    SurfaceTexture_createReleaseFence createFence,
+                                    SurfaceTexture_fenceWait fenceWait,
+                                    void* fencePassThroughHandle);
+
+    /**
+     * takeConsumerOwnership attaches a SurfaceTexture that is currently in the
+     * 'detached' state to a consumer context (usually HWUI RenderThread).
+     */
+    void takeConsumerOwnership();
+
+    /**
+     * releaseConsumerOwnership detaches a SurfaceTexture from a consumer
+     * context (usually HWUI RenderThread).
+     */
+    void releaseConsumerOwnership();
+
+protected:
+    /**
+     * abandonLocked overrides the ConsumerBase method to clear
+     * mCurrentTextureImage in addition to the ConsumerBase behavior.
+     */
+    virtual void abandonLocked();
+
+    /**
+     * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
+     * specific info in addition to the ConsumerBase behavior.
+     */
+    virtual void dumpLocked(String8& result, const char* prefix) const override;
+
+    /**
+     * acquireBufferLocked overrides the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase behavior.
+     */
+    virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+                                         uint64_t maxFrameNumber = 0) override;
+
+    /**
+     * releaseBufferLocked overrides the ConsumerBase method to update the
+     * mEglSlots array in addition to the ConsumerBase.
+     */
+    virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+                                         EGLDisplay display, EGLSyncKHR eglFence) override;
+
+    /**
+     * 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 EGLImage in that slot.  Otherwise it has no effect.
+     *
+     * This method must be called with mMutex locked.
+     */
+    virtual void freeBufferLocked(int slotIndex);
+
+    /**
+     * computeCurrentTransformMatrixLocked computes the transform matrix for the
+     * current texture.  It uses mCurrentTransform and the current GraphicBuffer
+     * to compute this matrix and stores it in mCurrentTransformMatrix.
+     * mCurrentTextureImage must not be NULL.
+     */
+    void computeCurrentTransformMatrixLocked();
+
+    /**
+     * The default consumer usage flags that SurfaceTexture always sets on its
+     * BufferQueue instance; these will be OR:d with any additional flags passed
+     * from the SurfaceTexture user. In particular, SurfaceTexture will always
+     * consume buffers as hardware textures.
+     */
+    static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+    /**
+     * mCurrentCrop is the crop rectangle that applies to the current texture.
+     * It gets set each time updateTexImage is called.
+     */
+    Rect mCurrentCrop;
+
+    /**
+     * mCurrentTransform is the transform identifier for the current texture. It
+     * gets set each time updateTexImage is called.
+     */
+    uint32_t mCurrentTransform;
+
+    /**
+     * mCurrentScalingMode is the scaling mode for the current texture. It gets
+     * set each time updateTexImage is called.
+     */
+    uint32_t mCurrentScalingMode;
+
+    /**
+     * mCurrentFence is the fence received from BufferQueue in updateTexImage.
+     */
+    sp<Fence> mCurrentFence;
+
+    /**
+     * The FenceTime wrapper around mCurrentFence.
+     */
+    std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
+    /**
+     * mCurrentTransformMatrix is the transform matrix for the current texture.
+     * It gets computed by computeTransformMatrix each time updateTexImage is
+     * called.
+     */
+    float mCurrentTransformMatrix[16];
+
+    /**
+     * mCurrentTimestamp is the timestamp for the current texture. It
+     * gets set each time updateTexImage is called.
+     */
+    int64_t mCurrentTimestamp;
+
+    /**
+     * mCurrentDataSpace is the dataspace for the current texture. It
+     * gets set each time updateTexImage is called.
+     */
+    android_dataspace mCurrentDataSpace;
+
+    /**
+     * mCurrentFrameNumber is the frame counter for the current texture.
+     * It gets set each time updateTexImage is called.
+     */
+    uint64_t mCurrentFrameNumber;
+
+    uint32_t mDefaultWidth, mDefaultHeight;
+
+    /**
+     * mFilteringEnabled indicates whether the transform matrix is computed for
+     * use with bilinear filtering. It defaults to true and is changed by
+     * setFilteringEnabled().
+     */
+    bool mFilteringEnabled;
+
+    /**
+     * mTexName is the name of the OpenGL texture to which streamed images will
+     * be bound when updateTexImage is called. It is set at construction time
+     * and can be changed with a call to attachToContext.
+     */
+    uint32_t mTexName;
+
+    /**
+     * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
+     * extension should be used to prevent buffers from being dequeued before
+     * it's safe for them to be written. It gets set at construction time and
+     * never changes.
+     */
+    const bool mUseFenceSync;
+
+    /**
+     * mTexTarget is the GL texture target with which the GL texture object is
+     * associated.  It is set in the constructor and never changed.  It is
+     * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
+     * Browser.  In that case it is set to GL_TEXTURE_2D to allow
+     * glCopyTexSubImage to read from the texture.  This is a hack to work
+     * around a GL driver limitation on the number of FBO attachments, which the
+     * browser's tile cache exceeds.
+     */
+    const uint32_t mTexTarget;
+
+    /**
+     * mCurrentTexture is the buffer slot index of the buffer that is currently
+     * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+     * indicating that no buffer slot is currently bound to the texture. Note,
+     * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+     * that no buffer is bound to the texture. A call to setBufferCount will
+     * reset mCurrentTexture to INVALID_BUFFER_SLOT.
+     */
+    int mCurrentTexture;
+
+    enum class OpMode { detached, attachedToConsumer, attachedToGL };
+    /**
+     * mOpMode indicates whether the SurfaceTexture is currently attached to
+     * an OpenGL ES context or the consumer context.  For legacy reasons, this
+     * is initialized to, "attachedToGL" indicating that the SurfaceTexture is
+     * considered to be attached to whatever GL context is current at the time
+     * of the first updateTexImage call.
+     * It is set to "detached" by detachFromContext, and then set to
+     * "attachedToGL" again by attachToContext.
+     * takeConsumerOwnership/releaseConsumerOwnership are used to attach/detach
+     * from a consumer context - usually HWUI RenderThread.
+     */
+    OpMode mOpMode;
+
+    /**
+     * mEGLConsumer has SurfaceTexture logic used when attached to GL context.
+     */
+    EGLConsumer mEGLConsumer;
+
+    /**
+     * mImageConsumer has SurfaceTexture logic used when attached to a consumer
+     * context (usually HWUI RenderThread).
+     */
+    ImageConsumer mImageConsumer;
+
+    friend class ImageConsumer;
+    friend class EGLConsumer;
+};
+
+// ----------------------------------------------------------------------------
+} // namespace android
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
new file mode 100644
index 0000000..6a94a77
--- /dev/null
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+#define _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <system/graphics.h>
+
+// This file provides a facade API on top of SurfaceTexture, which avoids using
+// C++ types. This is still a C++ unstable API though. Ideally features here
+// will be exposed via public NDK API and this file will be deleted.
+
+struct ASurfaceTexture;
+
+namespace android {
+
+/**
+ * ASurfaceTexture_getCurrentTextureTarget returns the texture target of the
+ * current texture.
+ */
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_takeConsumerOwnership attaches an ASurfaceTexture that is
+ * currently in the 'detached' state to a consumer context.
+ */
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * ASurfaceTexture_releaseConsumerOwnership detaches a SurfaceTexture from
+ * a consumer context.
+ */
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* st);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It creates a
+ * fence that is signalled when the previous buffer is no longer in use by the
+ * consumer (usually HWUI RenderThread) and can be written to by the producer.
+ */
+typedef int (*ASurfaceTexture_createReleaseFence)(bool useFenceSync, EGLSyncKHR* eglFence,
+                                                  EGLDisplay* display, int* releaseFence,
+                                                  void* fencePassThroughHandle);
+
+/**
+ * Callback function needed by ASurfaceTexture_dequeueBuffer. It waits for the
+ * new buffer fence to signal before issuing any draw commands.
+ */
+typedef int (*ASurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
+
+/**
+ * ASurfaceTexture_dequeueBuffer returns the next available AHardwareBuffer.
+ */
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+                                               android_dataspace* outDataspace,
+                                               float* outTransformMatrix, bool* outNewContent,
+                                               ASurfaceTexture_createReleaseFence createFence,
+                                               ASurfaceTexture_fenceWait fenceWait,
+                                               void* fencePassThroughHandle);
+
+} // namespace android
+
+#endif // _ANDROID_GRAPHICS_SURFACE_TEXTURE_PLATFORM_H
diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt
new file mode 100644
index 0000000..483fb25
--- /dev/null
+++ b/libs/nativedisplay/libnativedisplay.map.txt
@@ -0,0 +1,47 @@
+LIBNATIVEDISPLAY {
+  global:
+    AChoreographer_getInstance; # apex # introduced=30
+    AChoreographer_postFrameCallback; # apex # introduced=30
+    AChoreographer_postFrameCallbackDelayed; # apex # introduced=30
+    AChoreographer_postFrameCallback64; # apex # introduced=30
+    AChoreographer_postFrameCallbackDelayed64; # apex # introduced=30
+    AChoreographer_registerRefreshRateCallback; # apex # introduced=30
+    AChoreographer_unregisterRefreshRateCallback; # apex # introduced=30
+    AChoreographer_create; # apex # introduced=30
+    AChoreographer_destroy; # apex # introduced=30
+    AChoreographer_getFd; # apex # introduced=30
+    AChoreographer_handlePendingEvents; # apex # introduced=30
+    ASurfaceTexture_fromSurfaceTexture; # apex # introduced=30
+    ASurfaceTexture_release; # apex # introduced=30
+  local:
+    *;
+};
+
+LIBNATIVEDISPLAY_PLATFORM {
+  global:
+    extern "C++" {
+      android::ADisplay_acquirePhysicalDisplays*;
+      android::ADisplay_release*;
+      android::ADisplay_getMaxSupportedFps*;
+      android::ADisplay_getDisplayType*;
+      android::ADisplay_getPreferredWideColorFormat*;
+      android::ADisplay_getCurrentConfig*;
+      android::ADisplayConfig_getDensity*;
+      android::ADisplayConfig_getWidth*;
+      android::ADisplayConfig_getHeight*;
+      android::ADisplayConfig_getFps*;
+      android::ADisplayConfig_getCompositorOffsetNanos*;
+      android::ADisplayConfig_getAppVsyncOffsetNanos*;
+      android::ASurfaceTexture_getCurrentTextureTarget*;
+      android::ASurfaceTexture_takeConsumerOwnership*;
+      android::ASurfaceTexture_releaseConsumerOwnership*;
+      android::ASurfaceTexture_dequeueBuffer*;
+      android::SurfaceTexture*;
+    };
+    ASurfaceTexture_acquireANativeWindow;
+    ASurfaceTexture_attachToGLContext;
+    ASurfaceTexture_detachFromGLContext;
+    ASurfaceTexture_getTimestamp;
+    ASurfaceTexture_getTransformMatrix;
+    ASurfaceTexture_updateTexImage;
+} LIBNATIVEDISPLAY;
diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
new file mode 100644
index 0000000..2f31888
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -0,0 +1,674 @@
+/*
+ * 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 GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <surfacetexture/EGLConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+#include <inttypes.h>
+#include <private/gui/SyncFeatures.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+static const struct {
+    uint32_t width, height;
+    char const* bits;
+} kDebugData = {15, 12,
+                "_______________"
+                "_______________"
+                "_____XX_XX_____"
+                "__X_X_____X_X__"
+                "__X_XXXXXXX_X__"
+                "__XXXXXXXXXXX__"
+                "___XX_XXX_XX___"
+                "____XXXXXXX____"
+                "_____X___X_____"
+                "____X_____X____"
+                "_______________"
+                "_______________"};
+
+Mutex EGLConsumer::sStaticInitLock;
+sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer;
+
+static bool hasEglProtectedContentImpl() {
+    EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+    size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
+    size_t extsLen = strlen(exts);
+    bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
+    bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1);
+    bool atEnd = (cropExtLen + 1) < extsLen &&
+            !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1));
+    bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
+    return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglProtectedContent() {
+    // Only compute whether the extension is present once the first time this
+    // function is called.
+    static bool hasIt = hasEglProtectedContentImpl();
+    return hasIt;
+}
+
+EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {}
+
+status_t EGLConsumer::updateTexImage(SurfaceTexture& st) {
+    // Make sure the EGL state is the same as in previous calls.
+    status_t err = checkAndUpdateEglStateLocked(st);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    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.
+    err = st.acquireBufferLocked(&item, 0);
+    if (err != NO_ERROR) {
+        if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+            // We always bind the texture even if we don't update its contents.
+            EGC_LOGV("updateTexImage: no buffers were available");
+            glBindTexture(st.mTexTarget, st.mTexName);
+            err = NO_ERROR;
+        } else {
+            EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
+        }
+        return err;
+    }
+
+    // Release the previous buffer.
+    err = updateAndReleaseLocked(item, nullptr, st);
+    if (err != NO_ERROR) {
+        // We always bind the texture.
+        glBindTexture(st.mTexTarget, st.mTexName);
+        return err;
+    }
+
+    // Bind the new buffer to the GL texture, and wait until it's ready.
+    return bindTextureImageLocked(st);
+}
+
+status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) {
+    // Make sure the EGL state is the same as in previous calls.
+    status_t err = NO_ERROR;
+
+    // if we're detached, no need to validate EGL's state -- we won't use it.
+    if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+        err = checkAndUpdateEglStateLocked(st, true);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    // Update the EGLConsumer state.
+    int buf = st.mCurrentTexture;
+    if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
+        EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode);
+
+        // if we're detached, we just use the fence that was created in
+        // detachFromContext() so... basically, nothing more to do here.
+        if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+            // Do whatever sync ops we need to do before releasing the slot.
+            err = syncForReleaseLocked(mEglDisplay, st);
+            if (err != NO_ERROR) {
+                EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
+                return err;
+            }
+        }
+
+        err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay,
+                                     EGL_NO_SYNC_KHR);
+        if (err < NO_ERROR) {
+            EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err);
+            return err;
+        }
+
+        if (mReleasedTexImage == nullptr) {
+            mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+        }
+
+        st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+        mCurrentTextureImage = mReleasedTexImage;
+        st.mCurrentCrop.makeInvalid();
+        st.mCurrentTransform = 0;
+        st.mCurrentTimestamp = 0;
+        st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
+        st.mCurrentFence = Fence::NO_FENCE;
+        st.mCurrentFenceTime = FenceTime::NO_FENCE;
+
+        // detached, don't touch the texture (and we may not even have an
+        // EGLDisplay here.
+        if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+            // This binds a dummy buffer (mReleasedTexImage).
+            status_t result = bindTextureImageLocked(st);
+            if (result != NO_ERROR) {
+                return result;
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() {
+    Mutex::Autolock _l(sStaticInitLock);
+    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(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+                                  GraphicBuffer::USAGE_SW_WRITE_RARELY,
+                                  "[EGLConsumer debug texture]");
+        uint32_t* bits;
+        buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
+        uint32_t stride = buffer->getStride();
+        uint32_t height = buffer->getHeight();
+        memset(bits, 0, stride * height * 4);
+        for (uint32_t y = 0; y < kDebugData.height; y++) {
+            for (uint32_t x = 0; x < kDebugData.width; x++) {
+                bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000
+                                                                             : 0xFFFFFFFF;
+            }
+            bits += stride;
+        }
+        buffer->unlock();
+        sReleasedTexImageBuffer = buffer;
+    }
+    return sReleasedTexImageBuffer;
+}
+
+void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) {
+    // 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).
+    int slot = item->mSlot;
+    if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) {
+        mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+    }
+}
+
+void EGLConsumer::onReleaseBufferLocked(int buf) {
+    mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+                                             SurfaceTexture& st) {
+    status_t err = NO_ERROR;
+
+    int slot = item.mSlot;
+
+    if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) {
+        EGC_LOGE("updateAndRelease: EGLConsumer is not attached to an OpenGL "
+                 "ES context");
+        st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+        return INVALID_OPERATION;
+    }
+
+    // Confirm state.
+    err = checkAndUpdateEglStateLocked(st);
+    if (err != NO_ERROR) {
+        st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+        return err;
+    }
+
+    // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+    // if nessessary, for the gralloc buffer currently in the slot in
+    // 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);
+    if (err != NO_ERROR) {
+        EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
+                 slot);
+        st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+        return UNKNOWN_ERROR;
+    }
+
+    // Do whatever sync ops we need to do before releasing the old slot.
+    if (slot != st.mCurrentTexture) {
+        err = syncForReleaseLocked(mEglDisplay, st);
+        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.
+            // As we are still under lock since acquireBuffer, it is safe to
+            // release by slot.
+            st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay,
+                                   EGL_NO_SYNC_KHR);
+            return err;
+        }
+    }
+
+    EGC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture,
+             mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle()
+                                             : nullptr,
+             slot, st.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<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
+    // release old buffer
+    if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        if (pendingRelease == nullptr) {
+            status_t status =
+                    st.releaseBufferLocked(st.mCurrentTexture,
+                                           mCurrentTextureImage->graphicBuffer(), mEglDisplay,
+                                           mEglSlots[st.mCurrentTexture].mEglFence);
+            if (status < NO_ERROR) {
+                EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
+                         status);
+                err = status;
+                // keep going, with error raised [?]
+            }
+        } else {
+            pendingRelease->currentTexture = st.mCurrentTexture;
+            pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+            pendingRelease->display = mEglDisplay;
+            pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence;
+            pendingRelease->isPending = true;
+        }
+    }
+
+    // Update the EGLConsumer state.
+    st.mCurrentTexture = slot;
+    mCurrentTextureImage = nextTextureImage;
+    st.mCurrentCrop = item.mCrop;
+    st.mCurrentTransform = item.mTransform;
+    st.mCurrentScalingMode = item.mScalingMode;
+    st.mCurrentTimestamp = item.mTimestamp;
+    st.mCurrentDataSpace = item.mDataSpace;
+    st.mCurrentFence = item.mFence;
+    st.mCurrentFenceTime = item.mFenceTime;
+    st.mCurrentFrameNumber = item.mFrameNumber;
+
+    st.computeCurrentTransformMatrixLocked();
+
+    return err;
+}
+
+status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) {
+    if (mEglDisplay == EGL_NO_DISPLAY) {
+        ALOGE("bindTextureImage: invalid display");
+        return INVALID_OPERATION;
+    }
+
+    GLenum error;
+    while ((error = glGetError()) != GL_NO_ERROR) {
+        EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+    }
+
+    glBindTexture(st.mTexTarget, st.mTexName);
+    if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+        EGC_LOGE("bindTextureImage: no currently-bound texture");
+        return NO_INIT;
+    }
+
+    status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
+    if (err != NO_ERROR) {
+        EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+                 st.mCurrentTexture);
+        return UNKNOWN_ERROR;
+    }
+    mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+
+    // In the rare case that the display is terminated and then initialized
+    // again, we can't detect that the display changed (it didn't), but the
+    // image is invalid. In this case, repeat the exact same steps while
+    // forcing the creation of a new image.
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        glBindTexture(st.mTexTarget, st.mTexName);
+        status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
+        if (result != NO_ERROR) {
+            EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+                     st.mCurrentTexture);
+            return UNKNOWN_ERROR;
+        }
+        mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+        if ((error = glGetError()) != GL_NO_ERROR) {
+            EGC_LOGE("bindTextureImage: error binding external image: %#04x", error);
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    // Wait for the new buffer to be ready.
+    return doGLFenceWaitLocked(st);
+}
+
+status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) {
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (!contextCheck) {
+        // if this is the first time we're called, mEglDisplay/mEglContext have
+        // never been set, so don't error out (below).
+        if (mEglDisplay == EGL_NO_DISPLAY) {
+            mEglDisplay = dpy;
+        }
+        if (mEglContext == EGL_NO_CONTEXT) {
+            mEglContext = ctx;
+        }
+    }
+
+    if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) {
+        EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) {
+        EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    mEglDisplay = dpy;
+    mEglContext = ctx;
+    return NO_ERROR;
+}
+
+status_t EGLConsumer::detachFromContext(SurfaceTexture& st) {
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
+        EGC_LOGE("detachFromContext: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
+        EGC_LOGE("detachFromContext: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
+        status_t err = syncForReleaseLocked(dpy, st);
+        if (err != OK) {
+            return err;
+        }
+
+        glDeleteTextures(1, &st.mTexName);
+    }
+
+    mEglDisplay = EGL_NO_DISPLAY;
+    mEglContext = EGL_NO_CONTEXT;
+
+    return OK;
+}
+
+status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) {
+    // Initialize mCurrentTextureImage if there is a current buffer from past
+    // attached state.
+    int slot = st.mCurrentTexture;
+    if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+        if (!mEglSlots[slot].mEglImage.get()) {
+            mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+        }
+        mCurrentTextureImage = mEglSlots[slot].mEglImage;
+    }
+
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (dpy == EGL_NO_DISPLAY) {
+        EGC_LOGE("attachToContext: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (ctx == EGL_NO_CONTEXT) {
+        EGC_LOGE("attachToContext: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    // We need to bind the texture regardless of whether there's a current
+    // buffer.
+    glBindTexture(st.mTexTarget, GLuint(tex));
+
+    mEglDisplay = dpy;
+    mEglContext = ctx;
+    st.mTexName = tex;
+    st.mOpMode = SurfaceTexture::OpMode::attachedToGL;
+
+    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
+        // wait is redundant.
+        status_t err = bindTextureImageLocked(st);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
+status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) {
+    EGC_LOGV("syncForReleaseLocked");
+
+    if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        if (SyncFeatures::getInstance().useNativeFenceSync()) {
+            EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+            if (sync == EGL_NO_SYNC_KHR) {
+                EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError());
+                return UNKNOWN_ERROR;
+            }
+            glFlush();
+            int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync);
+            eglDestroySyncKHR(dpy, sync);
+            if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+                EGC_LOGE("syncForReleaseLocked: error dup'ing native fence "
+                         "fd: %#x",
+                         eglGetError());
+                return UNKNOWN_ERROR;
+            }
+            sp<Fence> fence(new Fence(fenceFd));
+            status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+                                                    mCurrentTextureImage->graphicBuffer(), fence);
+            if (err != OK) {
+                EGC_LOGE("syncForReleaseLocked: error adding release fence: "
+                         "%s (%d)",
+                         strerror(-err), err);
+                return err;
+            }
+        } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+            EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence;
+            if (fence != EGL_NO_SYNC_KHR) {
+                // There is already a fence for the current slot.  We need to
+                // wait on that before replacing it with another fence to
+                // ensure that all outstanding buffer accesses have completed
+                // before the producer accesses it.
+                EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+                if (result == EGL_FALSE) {
+                    EGC_LOGE("syncForReleaseLocked: error waiting for previous "
+                             "fence: %#x",
+                             eglGetError());
+                    return UNKNOWN_ERROR;
+                } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+                    EGC_LOGE("syncForReleaseLocked: timeout waiting for previous "
+                             "fence");
+                    return TIMED_OUT;
+                }
+                eglDestroySyncKHR(dpy, fence);
+            }
+
+            // Create a fence for the outstanding accesses in the current
+            // OpenGL ES context.
+            fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+            if (fence == EGL_NO_SYNC_KHR) {
+                EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError());
+                return UNKNOWN_ERROR;
+            }
+            glFlush();
+            mEglSlots[st.mCurrentTexture].mEglFence = fence;
+        }
+    }
+
+    return OK;
+}
+
+status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const {
+    EGLDisplay dpy = eglGetCurrentDisplay();
+    EGLContext ctx = eglGetCurrentContext();
+
+    if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) {
+        EGC_LOGE("doGLFenceWait: invalid current EGLDisplay");
+        return INVALID_OPERATION;
+    }
+
+    if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) {
+        EGC_LOGE("doGLFenceWait: invalid current EGLContext");
+        return INVALID_OPERATION;
+    }
+
+    if (st.mCurrentFence->isValid()) {
+        if (SyncFeatures::getInstance().useWaitSync() &&
+            SyncFeatures::getInstance().useNativeFenceSync()) {
+            // Create an EGLSyncKHR from the current fence.
+            int fenceFd = st.mCurrentFence->dup();
+            if (fenceFd == -1) {
+                EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno);
+                return -errno;
+            }
+            EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+            EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+            if (sync == EGL_NO_SYNC_KHR) {
+                close(fenceFd);
+                EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError());
+                return UNKNOWN_ERROR;
+            }
+
+            // 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(dpy, sync, 0);
+            EGLint eglErr = eglGetError();
+            eglDestroySyncKHR(dpy, sync);
+            if (eglErr != EGL_SUCCESS) {
+                EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr);
+                return UNKNOWN_ERROR;
+            }
+        } else {
+            status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked");
+            if (err != NO_ERROR) {
+                EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err);
+                return err;
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
+void EGLConsumer::onFreeBufferLocked(int slotIndex) {
+    mEglSlots[slotIndex].mEglImage.clear();
+}
+
+void EGLConsumer::onAbandonLocked() {
+    mCurrentTextureImage.clear();
+}
+
+EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer)
+      : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {}
+
+EGLConsumer::EglImage::~EglImage() {
+    if (mEglImage != EGL_NO_IMAGE_KHR) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+            ALOGE("~EglImage: eglDestroyImageKHR failed");
+        }
+        eglTerminate(mEglDisplay);
+    }
+}
+
+status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, 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;
+    if (haveImage && (displayInvalid || forceCreation)) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+            ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+        }
+        eglTerminate(mEglDisplay);
+        mEglImage = EGL_NO_IMAGE_KHR;
+        mEglDisplay = EGL_NO_DISPLAY;
+    }
+
+    // If there's no image, create one.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = eglDisplay;
+        mEglImage = createImage(mEglDisplay, mGraphicBuffer);
+    }
+
+    // Fail if we can't create a valid image.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = EGL_NO_DISPLAY;
+        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 UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+    glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage));
+}
+
+EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy,
+                                               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,
+            createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+            createProtectedImage ? EGL_TRUE : EGL_NONE,
+            EGL_NONE,
+    };
+    eglInitialize(dpy, nullptr, nullptr);
+    EGLImageKHR image =
+            eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+    if (image == EGL_NO_IMAGE_KHR) {
+        EGLint error = eglGetError();
+        ALOGE("error creating EGLImage: %#x", error);
+        eglTerminate(dpy);
+    }
+    return image;
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
new file mode 100644
index 0000000..16afc68
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 <gui/BufferQueue.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+
+// Macro for including the SurfaceTexture name in log messages
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+void ImageConsumer::onReleaseBufferLocked(int buf) {
+    mImageSlots[buf].eglFence() = EGL_NO_SYNC_KHR;
+}
+
+sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                               bool* outQueueEmpty, SurfaceTexture& st,
+                                               SurfaceTexture_createReleaseFence createFence,
+                                               SurfaceTexture_fenceWait fenceWait,
+                                               void* fencePassThroughHandle) {
+    BufferItem item;
+    status_t err;
+    err = st.acquireBufferLocked(&item, 0);
+    if (err != OK) {
+        if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
+            IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+        } else {
+            int slot = st.mCurrentTexture;
+            if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+                *outQueueEmpty = true;
+                *outDataspace = st.mCurrentDataSpace;
+                *outSlotid = slot;
+                return st.mSlots[slot].mGraphicBuffer;
+            }
+        }
+        return nullptr;
+    }
+
+    int slot = item.mSlot;
+    if (item.mFence->isValid()) {
+        // Wait on the producer fence for the buffer to be ready.
+        err = fenceWait(item.mFence->get(), fencePassThroughHandle);
+        if (err != OK) {
+            st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+                                   EGL_NO_SYNC_KHR);
+            return nullptr;
+        }
+    }
+
+    // Release old buffer.
+    if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
+        // If needed, set the released slot's fence to guard against a producer
+        // accessing the buffer before the outstanding accesses have completed.
+        int releaseFenceId = -1;
+        EGLDisplay display = EGL_NO_DISPLAY;
+        err = createFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), &display,
+                          &releaseFenceId, fencePassThroughHandle);
+        if (OK != err) {
+            st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+                                   EGL_NO_SYNC_KHR);
+            return nullptr;
+        }
+
+        if (releaseFenceId != -1) {
+            sp<Fence> releaseFence(new Fence(releaseFenceId));
+            status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+                                                    st.mSlots[st.mCurrentTexture].mGraphicBuffer,
+                                                    releaseFence);
+            if (err != OK) {
+                IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
+                st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+                                       EGL_NO_SYNC_KHR);
+                return nullptr;
+            }
+        }
+
+        // Finally release the old buffer.
+        status_t status =
+                st.releaseBufferLocked(st.mCurrentTexture,
+                                       st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
+                                       mImageSlots[st.mCurrentTexture].eglFence());
+        if (status < NO_ERROR) {
+            IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
+            err = status;
+            // Keep going, with error raised.
+        }
+    }
+
+    // Update the state.
+    st.mCurrentTexture = slot;
+    st.mCurrentCrop = item.mCrop;
+    st.mCurrentTransform = item.mTransform;
+    st.mCurrentScalingMode = item.mScalingMode;
+    st.mCurrentTimestamp = item.mTimestamp;
+    st.mCurrentDataSpace = item.mDataSpace;
+    st.mCurrentFence = item.mFence;
+    st.mCurrentFenceTime = item.mFenceTime;
+    st.mCurrentFrameNumber = item.mFrameNumber;
+    st.computeCurrentTransformMatrixLocked();
+
+    *outQueueEmpty = false;
+    *outDataspace = item.mDataSpace;
+    *outSlotid = slot;
+    return st.mSlots[slot].mGraphicBuffer;
+}
+
+} /* namespace android */
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
new file mode 100644
index 0000000..62db6d0
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -0,0 +1,490 @@
+/*
+ * 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 <cutils/compiler.h>
+#include <gui/BufferQueue.h>
+#include <surfacetexture/ImageConsumer.h>
+#include <surfacetexture/SurfaceTexture.h>
+#include <surfacetexture/surface_texture_platform.h>
+#include <math/mat4.h>
+#include <system/window.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+
+static const mat4 mtxIdentity;
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+                               uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+      : ConsumerBase(bq, isControlledByApp),
+        mCurrentCrop(Rect::EMPTY_RECT),
+        mCurrentTransform(0),
+        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+        mCurrentFence(Fence::NO_FENCE),
+        mCurrentTimestamp(0),
+        mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+        mCurrentFrameNumber(0),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mFilteringEnabled(true),
+        mTexName(tex),
+        mUseFenceSync(useFenceSync),
+        mTexTarget(texTarget),
+        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+        mOpMode(OpMode::attachedToGL) {
+    SFT_LOGV("SurfaceTexture");
+
+    memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
+                               bool useFenceSync, bool isControlledByApp)
+      : ConsumerBase(bq, isControlledByApp),
+        mCurrentCrop(Rect::EMPTY_RECT),
+        mCurrentTransform(0),
+        mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+        mCurrentFence(Fence::NO_FENCE),
+        mCurrentTimestamp(0),
+        mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
+        mCurrentFrameNumber(0),
+        mDefaultWidth(1),
+        mDefaultHeight(1),
+        mFilteringEnabled(true),
+        mTexName(0),
+        mUseFenceSync(useFenceSync),
+        mTexTarget(texTarget),
+        mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
+        mOpMode(OpMode::detached) {
+    SFT_LOGV("SurfaceTexture");
+
+    memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+    mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!");
+        return NO_INIT;
+    }
+    mDefaultWidth = w;
+    mDefaultHeight = h;
+    return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t SurfaceTexture::updateTexImage() {
+    ATRACE_CALL();
+    SFT_LOGV("updateTexImage");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!");
+        return NO_INIT;
+    }
+
+    return mEGLConsumer.updateTexImage(*this);
+}
+
+status_t SurfaceTexture::releaseTexImage() {
+    // releaseTexImage can be invoked even when not attached to a GL context.
+    ATRACE_CALL();
+    SFT_LOGV("releaseTexImage");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!");
+        return NO_INIT;
+    }
+
+    return mEGLConsumer.releaseTexImage(*this);
+}
+
+status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+                                             uint64_t maxFrameNumber) {
+    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    switch (mOpMode) {
+        case OpMode::attachedToConsumer:
+            break;
+        case OpMode::attachedToGL:
+            mEGLConsumer.onAcquireBufferLocked(item, *this);
+            break;
+        case OpMode::detached:
+            break;
+    }
+
+    return NO_ERROR;
+}
+
+status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer,
+                                             EGLDisplay display, EGLSyncKHR eglFence) {
+    // release the buffer if it hasn't already been discarded by the
+    // BufferQueue. This can happen, for example, when the producer of this
+    // buffer has reallocated the original buffer slot after this buffer
+    // was acquired.
+    status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence);
+    // We could be releasing an EGL/Vulkan buffer, even if not currently
+    // attached to a GL context.
+    mImageConsumer.onReleaseBufferLocked(buf);
+    mEGLConsumer.onReleaseBufferLocked(buf);
+    return err;
+}
+
+status_t SurfaceTexture::detachFromContext() {
+    ATRACE_CALL();
+    SFT_LOGV("detachFromContext");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("detachFromContext: abandoned SurfaceTexture");
+        return NO_INIT;
+    }
+
+    if (mOpMode != OpMode::attachedToGL) {
+        SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context");
+        return INVALID_OPERATION;
+    }
+
+    status_t err = mEGLConsumer.detachFromContext(*this);
+    if (err == OK) {
+        mOpMode = OpMode::detached;
+    }
+
+    return err;
+}
+
+status_t SurfaceTexture::attachToContext(uint32_t tex) {
+    ATRACE_CALL();
+    SFT_LOGV("attachToContext");
+    Mutex::Autolock lock(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("attachToContext: abandoned SurfaceTexture");
+        return NO_INIT;
+    }
+
+    if (mOpMode != OpMode::detached) {
+        SFT_LOGE("attachToContext: SurfaceTexture is already attached to a "
+                 "context");
+        return INVALID_OPERATION;
+    }
+
+    return mEGLConsumer.attachToContext(tex, *this);
+}
+
+void SurfaceTexture::takeConsumerOwnership() {
+    ATRACE_CALL();
+    Mutex::Autolock _l(mMutex);
+    if (mAbandoned) {
+        SFT_LOGE("attachToView: abandoned SurfaceTexture");
+        return;
+    }
+    if (mOpMode == OpMode::detached) {
+        mOpMode = OpMode::attachedToConsumer;
+
+        if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+            // release possible EGLConsumer texture cache
+            mEGLConsumer.onFreeBufferLocked(mCurrentTexture);
+            mEGLConsumer.onAbandonLocked();
+        }
+    } else {
+        SFT_LOGE("attachToView: already attached");
+    }
+}
+
+void SurfaceTexture::releaseConsumerOwnership() {
+    ATRACE_CALL();
+    Mutex::Autolock _l(mMutex);
+
+    if (mAbandoned) {
+        SFT_LOGE("detachFromView: abandoned SurfaceTexture");
+        return;
+    }
+
+    if (mOpMode == OpMode::attachedToConsumer) {
+        mOpMode = OpMode::detached;
+    } else {
+        SFT_LOGE("detachFromView: not attached to View");
+    }
+}
+
+uint32_t SurfaceTexture::getCurrentTextureTarget() const {
+    return mTexTarget;
+}
+
+void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+    Mutex::Autolock lock(mMutex);
+    memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::setFilteringEnabled(bool enabled) {
+    Mutex::Autolock lock(mMutex);
+    if (mAbandoned) {
+        SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+        return;
+    }
+    bool needsRecompute = mFilteringEnabled != enabled;
+    mFilteringEnabled = enabled;
+
+    if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+        SFT_LOGD("setFilteringEnabled called with no current item");
+    }
+
+    if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+        computeCurrentTransformMatrixLocked();
+    }
+}
+
+void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+    SFT_LOGV("computeCurrentTransformMatrixLocked");
+    sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
+            ? nullptr
+            : mSlots[mCurrentTexture].mGraphicBuffer;
+    if (buf == nullptr) {
+        SFT_LOGD("computeCurrentTransformMatrixLocked: no current item");
+    }
+    computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform,
+                           mFilteringEnabled);
+}
+
+void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+                                            const Rect& cropRect, uint32_t transform,
+                                            bool filtering) {
+    // Transform matrices
+    static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+    static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+    static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+
+    mat4 xform;
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+        xform *= mtxFlipH;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+        xform *= mtxFlipV;
+    }
+    if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+        xform *= mtxRot90;
+    }
+
+    if (!cropRect.isEmpty() && buf.get()) {
+        float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+        float bufferWidth = buf->getWidth();
+        float bufferHeight = buf->getHeight();
+        float shrinkAmount = 0.0f;
+        if (filtering) {
+            // In order to prevent bilinear sampling beyond the edge of the
+            // crop rectangle we may need to shrink it by 2 texels in each
+            // dimension.  Normally this would just need to take 1/2 a texel
+            // off each end, but because the chroma channels of YUV420 images
+            // are subsampled we may need to shrink the crop region by a whole
+            // texel on each side.
+            switch (buf->getPixelFormat()) {
+                case PIXEL_FORMAT_RGBA_8888:
+                case PIXEL_FORMAT_RGBX_8888:
+                case PIXEL_FORMAT_RGBA_FP16:
+                case PIXEL_FORMAT_RGBA_1010102:
+                case PIXEL_FORMAT_RGB_888:
+                case PIXEL_FORMAT_RGB_565:
+                case PIXEL_FORMAT_BGRA_8888:
+                    // We know there's no subsampling of any channels, so we
+                    // only need to shrink by a half a pixel.
+                    shrinkAmount = 0.5;
+                    break;
+
+                default:
+                    // If we don't recognize the format, we must assume the
+                    // worst case (that we care about), which is YUV420.
+                    shrinkAmount = 1.0;
+                    break;
+            }
+        }
+
+        // Only shrink the dimensions that are not the size of the buffer.
+        if (cropRect.width() < bufferWidth) {
+            tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+            sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth;
+        }
+        if (cropRect.height() < bufferHeight) {
+            ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight;
+            sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight;
+        }
+
+        mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1);
+        xform = crop * xform;
+    }
+
+    // SurfaceFlinger expects the top of its window textures to be at a Y
+    // coordinate of 0, so SurfaceTexture 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.
+    xform = mtxFlipV * xform;
+
+    memcpy(outTransform, xform.asArray(), sizeof(xform));
+}
+
+Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
+    Rect outCrop = crop;
+
+    uint32_t newWidth = static_cast<uint32_t>(crop.width());
+    uint32_t newHeight = static_cast<uint32_t>(crop.height());
+
+    if (newWidth * bufferHeight > newHeight * bufferWidth) {
+        newWidth = newHeight * bufferWidth / bufferHeight;
+        ALOGV("too wide: newWidth = %d", newWidth);
+    } else if (newWidth * bufferHeight < newHeight * bufferWidth) {
+        newHeight = newWidth * bufferHeight / bufferWidth;
+        ALOGV("too tall: newHeight = %d", newHeight);
+    }
+
+    uint32_t currentWidth = static_cast<uint32_t>(crop.width());
+    uint32_t currentHeight = static_cast<uint32_t>(crop.height());
+
+    // The crop is too wide
+    if (newWidth < currentWidth) {
+        uint32_t dw = currentWidth - newWidth;
+        auto halfdw = dw / 2;
+        outCrop.left += halfdw;
+        // Not halfdw because it would subtract 1 too few when dw is odd
+        outCrop.right -= (dw - halfdw);
+        // The crop is too tall
+    } else if (newHeight < currentHeight) {
+        uint32_t dh = currentHeight - newHeight;
+        auto halfdh = dh / 2;
+        outCrop.top += halfdh;
+        // Not halfdh because it would subtract 1 too few when dh is odd
+        outCrop.bottom -= (dh - halfdh);
+    }
+
+    ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,
+          outCrop.bottom);
+
+    return outCrop;
+}
+
+nsecs_t SurfaceTexture::getTimestamp() {
+    SFT_LOGV("getTimestamp");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTimestamp;
+}
+
+android_dataspace SurfaceTexture::getCurrentDataSpace() {
+    SFT_LOGV("getCurrentDataSpace");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentDataSpace;
+}
+
+uint64_t SurfaceTexture::getFrameNumber() {
+    SFT_LOGV("getFrameNumber");
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFrameNumber;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+    Mutex::Autolock lock(mMutex);
+    return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
+            ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+            : mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentTransform;
+}
+
+uint32_t SurfaceTexture::getCurrentScalingMode() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentScalingMode;
+}
+
+sp<Fence> SurfaceTexture::getCurrentFence() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFence;
+}
+
+std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const {
+    Mutex::Autolock lock(mMutex);
+    return mCurrentFenceTime;
+}
+
+void SurfaceTexture::freeBufferLocked(int slotIndex) {
+    SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+    if (slotIndex == mCurrentTexture) {
+        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+    }
+    // The slotIndex buffer could have EGL cache, but there is no way to tell
+    // for sure. Buffers can be freed after SurfaceTexture has detached from GL
+    // context or View.
+    mEGLConsumer.onFreeBufferLocked(slotIndex);
+    ConsumerBase::freeBufferLocked(slotIndex);
+}
+
+void SurfaceTexture::abandonLocked() {
+    SFT_LOGV("abandonLocked");
+    mEGLConsumer.onAbandonLocked();
+    ConsumerBase::abandonLocked();
+}
+
+status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) {
+    return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
+}
+
+void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
+    result.appendFormat("%smTexName=%d mCurrentTexture=%d\n"
+                        "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
+                        prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left,
+                        mCurrentCrop.top, mCurrentCrop.right, mCurrentCrop.bottom,
+                        mCurrentTransform);
+
+    ConsumerBase::dumpLocked(result, prefix);
+}
+
+sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+                                                float* outTransformMatrix, bool* outQueueEmpty,
+                                                SurfaceTexture_createReleaseFence createFence,
+                                                SurfaceTexture_fenceWait fenceWait,
+                                                void* fencePassThroughHandle) {
+    Mutex::Autolock _l(mMutex);
+    sp<GraphicBuffer> buffer;
+
+    if (mAbandoned) {
+        SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!");
+        return buffer;
+    }
+
+    if (mOpMode != OpMode::attachedToConsumer) {
+        SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View");
+        return buffer;
+    }
+
+    buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
+                                          createFence, fenceWait, fencePassThroughHandle);
+    memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+    return buffer;
+}
+
+} // namespace android
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
new file mode 100644
index 0000000..1670fbb
--- /dev/null
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 <android/surface_texture.h>
+#include <android/surface_texture_jni.h>
+
+#define LOG_TAG "ASurfaceTexture"
+
+#include <utils/Log.h>
+
+#include <gui/Surface.h>
+
+#include <surfacetexture/surface_texture_platform.h>
+#include <surfacetexture/SurfaceTexture.h>
+
+#include <mutex>
+
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+
+struct ASurfaceTexture {
+    android::sp<android::SurfaceTexture> consumer;
+    android::sp<android::IGraphicBufferProducer> producer;
+};
+
+using namespace android;
+
+const char* const kSurfaceTextureClassPathName = "android/graphics/SurfaceTexture";
+
+struct fields_t {
+    jfieldID  surfaceTexture;
+    jfieldID  producer;
+};
+static fields_t fields;
+static std::once_flag sInitFieldsOnce;
+
+#define ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID "mSurfaceTexture"
+#define ANDROID_GRAPHICS_PRODUCER_JNI_ID "mProducer"
+
+static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz)
+{
+    fields.surfaceTexture = env->GetFieldID(clazz,
+            ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID, "J");
+    if (fields.surfaceTexture == NULL) {
+        ALOGE("can't find android/graphics/SurfaceTexture.%s",
+                ANDROID_GRAPHICS_SURFACETEXTURE_JNI_ID);
+    }
+    fields.producer = env->GetFieldID(clazz,
+            ANDROID_GRAPHICS_PRODUCER_JNI_ID, "J");
+    if (fields.producer == NULL) {
+        ALOGE("can't find android/graphics/SurfaceTexture.%s",
+                ANDROID_GRAPHICS_PRODUCER_JNI_ID);
+    }
+}
+
+static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
+    jclass clazz = env->FindClass(class_name);
+    LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
+    return clazz;
+}
+
+static void register_android_graphics_SurfaceTexture(JNIEnv* env)
+{
+    // Cache some fields.
+    ScopedLocalRef<jclass> klass(env, FindClassOrDie(env, kSurfaceTextureClassPathName));
+    SurfaceTexture_classInit(env, klass.get());
+}
+
+static bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz) {
+    std::call_once(sInitFieldsOnce, [=]() {
+        register_android_graphics_SurfaceTexture(env);
+    });
+
+    jclass surfaceTextureClass = env->FindClass(kSurfaceTextureClassPathName);
+    return env->IsInstanceOf(thiz, surfaceTextureClass);
+}
+
+static sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
+    std::call_once(sInitFieldsOnce, [=]() {
+        register_android_graphics_SurfaceTexture(env);
+    });
+
+    return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
+}
+
+static sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
+    std::call_once(sInitFieldsOnce, [=]() {
+        register_android_graphics_SurfaceTexture(env);
+    });
+
+    return (IGraphicBufferProducer*)env->GetLongField(thiz, fields.producer);
+}
+
+// The following functions implement NDK API.
+ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) {
+    if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) {
+        return nullptr;
+    }
+    ASurfaceTexture* ast = new ASurfaceTexture;
+    ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture);
+    ast->producer = SurfaceTexture_getProducer(env, surfacetexture);
+    return ast;
+}
+
+ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) {
+    sp<Surface> surface = new Surface(st->producer);
+    ANativeWindow* win(surface.get());
+    ANativeWindow_acquire(win);
+    return win;
+}
+
+void ASurfaceTexture_release(ASurfaceTexture* st) {
+    delete st;
+}
+
+int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) {
+    return st->consumer->attachToContext(tex);
+}
+
+int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) {
+    return st->consumer->detachFromContext();
+}
+
+int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) {
+    return st->consumer->updateTexImage();
+}
+
+void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) {
+    st->consumer->getTransformMatrix(mtx);
+}
+
+int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) {
+    return st->consumer->getTimestamp();
+}
+
+// The following functions are private/unstable API.
+namespace android {
+
+unsigned int ASurfaceTexture_getCurrentTextureTarget(ASurfaceTexture* st) {
+    return st->consumer->getCurrentTextureTarget();
+}
+
+void ASurfaceTexture_takeConsumerOwnership(ASurfaceTexture* texture) {
+    texture->consumer->takeConsumerOwnership();
+}
+
+void ASurfaceTexture_releaseConsumerOwnership(ASurfaceTexture* texture) {
+    texture->consumer->releaseConsumerOwnership();
+}
+
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
+                                               android_dataspace* outDataspace,
+                                               float* outTransformMatrix, bool* outNewContent,
+                                               ASurfaceTexture_createReleaseFence createFence,
+                                               ASurfaceTexture_fenceWait fenceWait, void* handle) {
+    sp<GraphicBuffer> buffer;
+    *outNewContent = false;
+    bool queueEmpty;
+    do {
+        buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
+                                             &queueEmpty, createFence, fenceWait, handle);
+        if (!queueEmpty) {
+            *outNewContent = true;
+        }
+    } while (buffer.get() && (!queueEmpty));
+    return reinterpret_cast<AHardwareBuffer*>(buffer.get());
+}
+
+} // namespace android
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 8435dac..98b76fd 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -33,6 +33,12 @@
     return res < 0 ? res : value;
 }
 
+static int64_t query64(ANativeWindow* window, int what) {
+    int64_t value;
+    int res = window->perform(window, what, &value);
+    return res < 0 ? res : value;
+}
+
 static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) {
     bool supported = false;
     switch (dataSpace) {
@@ -132,6 +138,11 @@
     static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
     static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
     static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+    static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
+    static_assert(static_cast<int>(ADATASPACE_BT2020) == static_cast<int>(HAL_DATASPACE_BT2020));
+    static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
+    static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
+    static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
 
     if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
             !isDataSpaceValid(window, dataSpace)) {
@@ -147,6 +158,21 @@
     return query(window, NATIVE_WINDOW_DATASPACE);
 }
 
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) {
+    if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || frameRate < 0) {
+        return -EINVAL;
+    }
+    return native_window_set_frame_rate(window, frameRate);
+}
+
+void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
+    if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
+        return;
+    }
+    window->perform(window, NATIVE_WINDOW_ALLOCATE_BUFFERS);
+}
+
+
 /**************************************************************************************************
  * vndk-stable
  **************************************************************************************************/
@@ -262,3 +288,54 @@
 int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh) {
     return native_window_set_auto_refresh(window, autoRefresh);
 }
+
+int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation) {
+    return native_window_set_auto_prerotation(window, autoPrerotation);
+}
+
+/**************************************************************************************************
+ * apex-stable
+ **************************************************************************************************/
+
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window) {
+    return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window) {
+    return query64(window, NATIVE_WINDOW_GET_LAST_QUEUE_DURATION);
+}
+
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) {
+    return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START);
+}
+
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) {
+    return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout);
+}
+
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+                                             ANativeWindow_cancelBufferInterceptor interceptor,
+                                             void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+                                              ANativeWindow_dequeueBufferInterceptor interceptor,
+                                              void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+                                        ANativeWindow_performInterceptor interceptor, void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR, interceptor, data);
+}
+
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+                                            ANativeWindow_queueBufferInterceptor interceptor,
+                                            void* data) {
+    return window->perform(window, NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR, interceptor, data);
+}
+
+int64_t ANativeWindow_getNextFrameId(ANativeWindow* window) {
+    return query64(window, NATIVE_WINDOW_GET_NEXT_FRAME_ID);
+}
diff --git a/libs/nativewindow/TEST_MAPPING b/libs/nativewindow/TEST_MAPPING
new file mode 100644
index 0000000..3d7f3c2
--- /dev/null
+++ b/libs/nativewindow/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libnativewindow_test"
+    }
+  ]
+}
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index 2899bcf..e759513 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -101,6 +101,56 @@
      * Use full range, SMPTE 2084 (PQ) transfer and BT2020 standard
      */
     ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
+
+    /**
+     * Adobe RGB
+     *
+     * Use full range, gamma 2.2 transfer and Adobe RGB primaries
+     * Note: Application is responsible for gamma encoding the data as
+     * a 2.2 gamma encoding is not supported in HW.
+     */
+    ADATASPACE_ADOBE_RGB = 151715840, // STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2 | RANGE_FULL
+
+    /**
+     * ITU-R Recommendation 2020 (BT.2020)
+     *
+     * Ultra High-definition television
+     *
+     * Use full range, BT.709 transfer and BT2020 standard
+     */
+    ADATASPACE_BT2020 = 147193856, // STANDARD_BT2020 | TRANSFER_SMPTE_170M | RANGE_FULL
+
+    /**
+     * ITU-R Recommendation 709 (BT.709)
+     *
+     * High-definition television
+     *
+     * Use limited range, BT.709 transfer and BT.709 standard.
+     */
+    ADATASPACE_BT709 = 281083904, // STANDARD_BT709 | TRANSFER_SMPTE_170M | RANGE_LIMITED
+
+    /**
+     * SMPTE EG 432-1 and SMPTE RP 431-2.
+     *
+     * Digital Cinema DCI-P3
+     *
+     * Use full range, gamma 2.6 transfer and D65 DCI-P3 standard
+     * Note: Application is responsible for gamma encoding the data as
+     * a 2.6 gamma encoding is not supported in HW.
+     */
+    ADATASPACE_DCI_P3 = 155844608, // STANDARD_DCI_P3 | TRANSFER_GAMMA2_6 | RANGE_FULL
+
+    /**
+     * sRGB linear encoding:
+     *
+     * The red, green, and blue components are stored in sRGB space, but
+     * are linear, not gamma-encoded.
+     * The RGB primaries and the white point are the same as BT.709.
+     *
+     * The values are encoded using the full range ([0,255] for 8-bit) for all
+     * components.
+     */
+    ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
 };
 
 __END_DECLS
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 3e436e3..4b426c5 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -230,6 +230,50 @@
 
 #endif // __ANDROID_API__ >= 28
 
+#if __ANDROID_API__ >= 30
+
+/**
+ * Sets the intended frame rate for this window.
+ *
+ * On devices that are capable of running the display at different refresh
+ * rates, the system may choose a display refresh rate to better match this
+ * window's frame rate. Usage of this API won't introduce frame rate throttling,
+ * or affect other aspects of the application's frame production
+ * pipeline. However, because the system may change the display refresh rate,
+ * calls to this function may result in changes to Choreographer callback
+ * timings, and changes to the time interval at which the system releases
+ * buffers back to the application.
+ *
+ * Note that this only has an effect for windows presented on the display. If
+ * this ANativeWindow is consumed by something other than the system compositor,
+ * e.g. a media codec, this call has no effect.
+ *
+ * Available since API level 30.
+ *
+ * \param frameRate The intended frame rate of this window, in frames per
+ * second. 0 is a special value that indicates the app will accept the system's
+ * choice for the display frame rate, which is the default behavior if this
+ * function isn't called. The frameRate param does *not* need to be a valid
+ * refresh rate for this device's display - e.g., it's fine to pass 30fps to a
+ * device that can only run the display at 60fps.
+ *
+ * \return 0 for success, -EINVAL if the window or frame rate are invalid.
+ */
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) __INTRODUCED_IN(30);
+
+/**
+ * Provides a hint to the window that buffers should be preallocated ahead of
+ * time. Note that the window implementation is not guaranteed to preallocate
+ * any buffers, for instance if an implementation disallows allocation of new
+ * buffers, or if there is insufficient memory in the system to preallocate
+ * additional buffers
+ *
+ * Available since API level 30.
+ */
+void ANativeWindow_tryAllocateBuffers(ANativeWindow* window);
+
+#endif // __ANDROID_API__ >= 30
+
 #ifdef __cplusplus
 };
 #endif
diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h
new file mode 100644
index 0000000..02b886c
--- /dev/null
+++ b/libs/nativewindow/include/apex/window.h
@@ -0,0 +1,222 @@
+/*
+ * 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 <nativebase/nativebase.h>
+#include <stdarg.h>
+
+// apex is a superset of the NDK
+#include <android/native_window.h>
+
+__BEGIN_DECLS
+
+/*
+ * perform bits that can be used with ANativeWindow_perform()
+ *
+ * This is only to support the intercepting methods below - these should notbe
+ * used directly otherwise.
+ */
+enum ANativeWindowPerform {
+    // clang-format off
+    ANATIVEWINDOW_PERFORM_SET_USAGE            = 0,
+    ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY = 5,
+    ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT   = 9,
+    ANATIVEWINDOW_PERFORM_SET_USAGE64          = 30,
+    // clang-format on
+};
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_cancelBuffer is called.
+ */
+typedef int (*ANativeWindow_cancelBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                            int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_cancelBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_cancelBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_cancelBufferInterceptor)(ANativeWindow* window,
+                                                     ANativeWindow_cancelBufferFn cancelBuffer,
+                                                     void* data, ANativeWindowBuffer* buffer,
+                                                     int fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_dequeueBuffer is called.
+ */
+typedef int (*ANativeWindow_dequeueBufferFn)(ANativeWindow* window, ANativeWindowBuffer** buffer,
+                                             int* fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_dequeueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_dequeueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_dequeueBufferInterceptor)(ANativeWindow* window,
+                                                      ANativeWindow_dequeueBufferFn dequeueBuffer,
+                                                      void* data, ANativeWindowBuffer** buffer,
+                                                      int* fenceFd);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_perform is called.
+ */
+typedef int (*ANativeWindow_performFn)(ANativeWindow* window, int operation, va_list args);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_performFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_performFn if it were to be called.
+ */
+typedef int (*ANativeWindow_performInterceptor)(ANativeWindow* window,
+                                                ANativeWindow_performFn perform, void* data,
+                                                int operation, va_list args);
+
+/**
+ * Prototype of the function that an ANativeWindow implementation would call
+ * when ANativeWindow_queueBuffer is called.
+ */
+typedef int (*ANativeWindow_queueBufferFn)(ANativeWindow* window, ANativeWindowBuffer* buffer,
+                                           int fenceFd);
+
+/**
+ * Prototype of the function that intercepts an invocation of
+ * ANativeWindow_queueBufferFn, along with a data pointer that's passed by the
+ * caller who set the interceptor, as well as arguments that would be
+ * passed to ANativeWindow_queueBufferFn if it were to be called.
+ */
+typedef int (*ANativeWindow_queueBufferInterceptor)(ANativeWindow* window,
+                                                    ANativeWindow_queueBufferFn queueBuffer,
+                                                    void* data, ANativeWindowBuffer* buffer,
+                                                    int fenceFd);
+
+/**
+ * Registers an interceptor for ANativeWindow_cancelBuffer. Instead of calling
+ * the underlying cancelBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying cancelBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setCancelBufferInterceptor(ANativeWindow* window,
+                                             ANativeWindow_cancelBufferInterceptor interceptor,
+                                             void* data);
+
+/**
+ * Registers an interceptor for ANativeWindow_dequeueBuffer. Instead of calling
+ * the underlying dequeueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying dequeueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setDequeueBufferInterceptor(ANativeWindow* window,
+                                              ANativeWindow_dequeueBufferInterceptor interceptor,
+                                              void* data);
+/**
+ * Registers an interceptor for ANativeWindow_perform. Instead of calling
+ * the underlying perform function, instead the provided interceptor is
+ * called, which may optionally call the underlying perform function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setPerformInterceptor(ANativeWindow* window,
+                                        ANativeWindow_performInterceptor interceptor, void* data);
+/**
+ * Registers an interceptor for ANativeWindow_queueBuffer. Instead of calling
+ * the underlying queueBuffer function, instead the provided interceptor is
+ * called, which may optionally call the underlying queueBuffer function. An
+ * optional data pointer is also provided to side-channel additional arguments.
+ *
+ * Note that usage of this should only be used for specialized use-cases by
+ * either the system partition or to Mainline modules. This should never be
+ * exposed to NDK or LL-NDK.
+ *
+ * Returns NO_ERROR on success, -errno if registration failed.
+ */
+int ANativeWindow_setQueueBufferInterceptor(ANativeWindow* window,
+                                            ANativeWindow_queueBufferInterceptor interceptor,
+                                            void* data);
+
+/**
+ * Retrieves how long it took for the last time a buffer was dequeued.
+ *
+ * \return a negative value on error, otherwise returns the duration in
+ * nanoseconds
+ */
+int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window);
+
+/**
+ * Retrieves how long it took for the last time a buffer was queued.
+ *
+ * \return a negative value on error, otherwise returns the duration in
+ * nanoseconds.
+ */
+int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window);
+
+/**
+ * Retrieves the system time in nanoseconds when the last time a buffer
+ * was dequeued.
+ *
+ * \return a negative value on error, otherwise returns the duration in
+ * nanoseconds.
+ */
+int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window);
+
+/**
+ * Sets a timeout in nanoseconds for dequeue calls. All subsequent dequeue calls
+ * made by the window will return -ETIMEDOUT after the timeout if the dequeue
+ * takes too long.
+ *
+ * \return NO_ERROR on success, -errno on error.
+ */
+int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout);
+
+/**
+ * Provides a hint to the window that buffers should be preallocated ahead of
+ * time. Note that the window implementation is not guaranteed to preallocate
+ * any buffers, for instance if a private API disallows allocation of new
+ * buffers. As such no success/error status is returned.
+ */
+void ANativeWindow_allocateBuffers(ANativeWindow* window);
+
+/**
+ * Retrieves an identifier for the next frame to be queued by this window.
+ *
+ * \return -errno on error, otherwise returns the next frame id.
+ */
+int64_t ANativeWindow_getNextFrameId(ANativeWindow* window);
+
+__END_DECLS
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 61590e0..f686147 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -34,14 +34,15 @@
 #include <cutils/native_handle.h>
 #include <errno.h>
 #include <limits.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/cdefs.h>
 #include <system/graphics.h>
 #include <unistd.h>
-#include <stdbool.h>
 
-// system/window.h is a superset of the vndk
+// system/window.h is a superset of the vndk and apex apis
+#include <apex/window.h>
 #include <vndk/window.h>
 
 
@@ -62,9 +63,9 @@
 
 /* attributes queriable with query() */
 enum {
-    NATIVE_WINDOW_WIDTH     = 0,
-    NATIVE_WINDOW_HEIGHT    = 1,
-    NATIVE_WINDOW_FORMAT    = 2,
+    NATIVE_WINDOW_WIDTH = 0,
+    NATIVE_WINDOW_HEIGHT = 1,
+    NATIVE_WINDOW_FORMAT = 2,
 
     /* see ANativeWindowQuery in vndk/window.h */
     NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS,
@@ -92,7 +93,6 @@
      */
     NATIVE_WINDOW_CONCRETE_TYPE = 5,
 
-
     /*
      * Default width and height of ANativeWindow buffers, these are the
      * dimensions of the window buffers irrespective of the
@@ -147,11 +147,15 @@
 
     /*
      * Returns the duration of the last dequeueBuffer call in microseconds
+     * Deprecated: please use NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION in
+     * perform() instead, which supports nanosecond precision.
      */
     NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14,
 
     /*
      * Returns the duration of the last queueBuffer call in microseconds
+     * Deprecated: please use NATIVE_WINDOW_GET_LAST_QUEUE_DURATION in
+     * perform() instead, which supports nanosecond precision.
      */
     NATIVE_WINDOW_LAST_QUEUE_DURATION = 15,
 
@@ -203,41 +207,53 @@
  */
 enum {
     // clang-format off
-    NATIVE_WINDOW_SET_USAGE                     =  0,   /* deprecated */
-    NATIVE_WINDOW_CONNECT                       =  1,   /* deprecated */
-    NATIVE_WINDOW_DISCONNECT                    =  2,   /* deprecated */
-    NATIVE_WINDOW_SET_CROP                      =  3,   /* private */
-    NATIVE_WINDOW_SET_BUFFER_COUNT              =  4,
-    NATIVE_WINDOW_SET_BUFFERS_GEOMETRY          =  5,   /* deprecated */
-    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM         =  6,
-    NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP         =  7,
-    NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS        =  8,
-    NATIVE_WINDOW_SET_BUFFERS_FORMAT            =  9,
-    NATIVE_WINDOW_SET_SCALING_MODE              = 10,   /* private */
-    NATIVE_WINDOW_LOCK                          = 11,   /* private */
-    NATIVE_WINDOW_UNLOCK_AND_POST               = 12,   /* private */
-    NATIVE_WINDOW_API_CONNECT                   = 13,   /* private */
-    NATIVE_WINDOW_API_DISCONNECT                = 14,   /* private */
-    NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS   = 15,   /* private */
-    NATIVE_WINDOW_SET_POST_TRANSFORM_CROP       = 16,   /* deprecated, unimplemented */
-    NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM  = 17,   /* private */
-    NATIVE_WINDOW_SET_SIDEBAND_STREAM           = 18,
-    NATIVE_WINDOW_SET_BUFFERS_DATASPACE         = 19,
-    NATIVE_WINDOW_SET_SURFACE_DAMAGE            = 20,   /* private */
-    NATIVE_WINDOW_SET_SHARED_BUFFER_MODE        = 21,
-    NATIVE_WINDOW_SET_AUTO_REFRESH              = 22,
-    NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION    = 23,
-    NATIVE_WINDOW_GET_NEXT_FRAME_ID             = 24,
-    NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS       = 25,
-    NATIVE_WINDOW_GET_COMPOSITOR_TIMING         = 26,
-    NATIVE_WINDOW_GET_FRAME_TIMESTAMPS          = 27,
-    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT        = 28,
-    NATIVE_WINDOW_GET_HDR_SUPPORT               = 29,
-    NATIVE_WINDOW_SET_USAGE64                   = 30,
-    NATIVE_WINDOW_GET_CONSUMER_USAGE64          = 31,
-    NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32,
-    NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33,
+    NATIVE_WINDOW_SET_USAGE                       =  ANATIVEWINDOW_PERFORM_SET_USAGE,   /* deprecated */
+    NATIVE_WINDOW_CONNECT                         =  1,   /* deprecated */
+    NATIVE_WINDOW_DISCONNECT                      =  2,   /* deprecated */
+    NATIVE_WINDOW_SET_CROP                        =  3,   /* private */
+    NATIVE_WINDOW_SET_BUFFER_COUNT                =  4,
+    NATIVE_WINDOW_SET_BUFFERS_GEOMETRY            =  ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY,   /* deprecated */
+    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM           =  6,
+    NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP           =  7,
+    NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS          =  8,
+    NATIVE_WINDOW_SET_BUFFERS_FORMAT              =  ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT,
+    NATIVE_WINDOW_SET_SCALING_MODE                = 10,   /* private */
+    NATIVE_WINDOW_LOCK                            = 11,   /* private */
+    NATIVE_WINDOW_UNLOCK_AND_POST                 = 12,   /* private */
+    NATIVE_WINDOW_API_CONNECT                     = 13,   /* private */
+    NATIVE_WINDOW_API_DISCONNECT                  = 14,   /* private */
+    NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS     = 15,   /* private */
+    NATIVE_WINDOW_SET_POST_TRANSFORM_CROP         = 16,   /* deprecated, unimplemented */
+    NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM    = 17,   /* private */
+    NATIVE_WINDOW_SET_SIDEBAND_STREAM             = 18,
+    NATIVE_WINDOW_SET_BUFFERS_DATASPACE           = 19,
+    NATIVE_WINDOW_SET_SURFACE_DAMAGE              = 20,   /* private */
+    NATIVE_WINDOW_SET_SHARED_BUFFER_MODE          = 21,
+    NATIVE_WINDOW_SET_AUTO_REFRESH                = 22,
+    NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION      = 23,
+    NATIVE_WINDOW_GET_NEXT_FRAME_ID               = 24,
+    NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS         = 25,
+    NATIVE_WINDOW_GET_COMPOSITOR_TIMING           = 26,
+    NATIVE_WINDOW_GET_FRAME_TIMESTAMPS            = 27,
+    NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT          = 28,
+    NATIVE_WINDOW_GET_HDR_SUPPORT                 = 29,
+    NATIVE_WINDOW_SET_USAGE64                     = ANATIVEWINDOW_PERFORM_SET_USAGE64,
+    NATIVE_WINDOW_GET_CONSUMER_USAGE64            = 31,
+    NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA  = 32,
+    NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA   = 33,
     NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34,
+    NATIVE_WINDOW_SET_AUTO_PREROTATION            = 35,
+    NATIVE_WINDOW_GET_LAST_DEQUEUE_START          = 36,    /* private */
+    NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT             = 37,    /* private */
+    NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION       = 38,    /* private */
+    NATIVE_WINDOW_GET_LAST_QUEUE_DURATION         = 39,    /* private */
+    NATIVE_WINDOW_SET_FRAME_RATE                  = 40,
+    NATIVE_WINDOW_SET_CANCEL_INTERCEPTOR          = 41,    /* private */
+    NATIVE_WINDOW_SET_DEQUEUE_INTERCEPTOR         = 42,    /* private */
+    NATIVE_WINDOW_SET_PERFORM_INTERCEPTOR         = 43,    /* private */
+    NATIVE_WINDOW_SET_QUEUE_INTERCEPTOR           = 44,    /* private */
+    NATIVE_WINDOW_ALLOCATE_BUFFERS                = 45,    /* private */
+    NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER          = 46,    /* private */
     // clang-format on
 };
 
@@ -985,4 +1001,52 @@
     return window->perform(window, NATIVE_WINDOW_GET_CONSUMER_USAGE64, outUsage);
 }
 
+/*
+ * native_window_set_auto_prerotation(..., autoPrerotation)
+ * Enable/disable the auto prerotation at buffer allocation when the buffer size
+ * is driven by the consumer.
+ *
+ * When buffer size is driven by the consumer and the transform hint specifies
+ * a 90 or 270 degree rotation, if auto prerotation is enabled, the width and
+ * height used for dequeueBuffer will be additionally swapped.
+ */
+static inline int native_window_set_auto_prerotation(struct ANativeWindow* window,
+                                                     bool autoPrerotation) {
+    return window->perform(window, NATIVE_WINDOW_SET_AUTO_PREROTATION, autoPrerotation);
+}
+
+static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate) {
+    return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Candidates for APEX visibility
+// These functions are planned to be made stable for APEX modules, but have not
+// yet been stabilized to a specific api version.
+// ------------------------------------------------------------------------------------------------
+
+/**
+ * Retrieves the last queued buffer for this window, along with the fence that
+ * fires when the buffer is ready to be read, and the 4x4 coordinate
+ * transform matrix that should be applied to the buffer's content. The
+ * transform matrix is represented in column-major order.
+ *
+ * If there was no buffer previously queued, then outBuffer will be NULL and
+ * the value of outFence will be -1.
+ *
+ * Note that if outBuffer is not NULL, then the caller will hold a reference
+ * onto the buffer. Accordingly, the caller must call AHardwareBuffer_release
+ * when the buffer is no longer needed so that the system may reclaim the
+ * buffer.
+ *
+ * \return NO_ERROR on success.
+ * \return NO_MEMORY if there was insufficient memory.
+ */
+static inline int ANativeWindow_getLastQueuedBuffer(ANativeWindow* window,
+                                                    AHardwareBuffer** outBuffer, int* outFence,
+                                                    float outTransformMatrix[16]) {
+    return window->perform(window, NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER, outBuffer, outFence,
+                           outTransformMatrix);
+}
+
 __END_DECLS
diff --git a/libs/nativewindow/include/vndk/window.h b/libs/nativewindow/include/vndk/window.h
index 995ba44..500052c 100644
--- a/libs/nativewindow/include/vndk/window.h
+++ b/libs/nativewindow/include/vndk/window.h
@@ -316,6 +316,15 @@
  */
 int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh);
 
+/*
+ * Enable/disable the auto prerotation at buffer allocation when the buffer size
+ * is driven by the consumer.
+ *
+ * When buffer size is driven by the consumer and the transform hint specifies
+ * a 90 or 270 degree rotation, if auto prerotation is enabled, the width and
+ * height used for dequeueBuffer will be additionally swapped.
+ */
+int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation);
 
 /*****************************************************************************/
 
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index db1c9b7..154eb8e 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -22,12 +22,21 @@
     ANativeWindow_getBuffersDataSpace; # introduced=28
     ANativeWindow_getFormat;
     ANativeWindow_getHeight;
+    ANativeWindow_getLastDequeueDuration; # apex # introduced=30
+    ANativeWindow_getLastDequeueStartTime; # apex # introduced=30
+    ANativeWindow_getLastQueueDuration; # apex # introduced=30
+    ANativeWindow_getNextFrameId; # apex # introduced=30
     ANativeWindow_getWidth;
     ANativeWindow_lock;
     ANativeWindow_query; # llndk
     ANativeWindow_queryf; # llndk
     ANativeWindow_queueBuffer; # llndk
+    ANativeWindow_setCancelBufferInterceptor; # apex # introduced=30
+    ANativeWindow_setDequeueBufferInterceptor; # apex # introduced=30
+    ANativeWindow_setPerformInterceptor; # apex # introduced=30
+    ANativeWindow_setQueueBufferInterceptor; # apex # introduced=30
     ANativeWindow_release;
+    ANativeWindow_setAutoPrerotation; # llndk
     ANativeWindow_setAutoRefresh; # llndk
     ANativeWindow_setBufferCount; # llndk
     ANativeWindow_setBuffersDataSpace; # introduced=28
@@ -36,9 +45,12 @@
     ANativeWindow_setBuffersGeometry;
     ANativeWindow_setBuffersTimestamp; # llndk
     ANativeWindow_setBuffersTransform;
+    ANativeWindow_setDequeueTimeout; # apex # introduced=30
     ANativeWindow_setSharedBufferMode; # llndk
     ANativeWindow_setSwapInterval; # llndk
+    ANativeWindow_setFrameRate; # introduced=30
     ANativeWindow_setUsage; # llndk
+    ANativeWindow_tryAllocateBuffers; # introduced=30
     ANativeWindow_unlockAndPost;
   local:
     *;
diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp
new file mode 100644
index 0000000..6cf8291
--- /dev/null
+++ b/libs/nativewindow/tests/ANativeWindowTest.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 "ANativeWindow_test"
+//#define LOG_NDEBUG 0
+
+#include <gtest/gtest.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/BufferQueue.h>
+#include <gui/Surface.h>
+#include <log/log.h>
+#include <sync/sync.h>
+// We need to use the private system apis since not everything is visible to
+// apexes yet.
+#include <system/window.h>
+
+using namespace android;
+
+class TestableSurface final : public Surface {
+public:
+    explicit TestableSurface(const sp<IGraphicBufferProducer>& bufferProducer)
+          : Surface(bufferProducer) {}
+
+    // Exposes the internal last dequeue duration that's stored on the Surface.
+    nsecs_t getLastDequeueDuration() const { return mLastDequeueDuration; }
+
+    // Exposes the internal last queue duration that's stored on the Surface.
+    nsecs_t getLastQueueDuration() const { return mLastQueueDuration; }
+
+    // Exposes the internal last dequeue start time that's stored on the Surface.
+    nsecs_t getLastDequeueStartTime() const { return mLastDequeueStartTime; }
+};
+
+class ANativeWindowTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+        BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+        mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN);
+        mWindow = new TestableSurface(mProducer);
+        const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU);
+        EXPECT_EQ(0, success);
+    }
+
+    void TearDown() override {
+        const ::testing::TestInfo* const test_info =
+                ::testing::UnitTest::GetInstance()->current_test_info();
+        ALOGV("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+        const int success = native_window_api_disconnect(mWindow.get(), NATIVE_WINDOW_API_CPU);
+        EXPECT_EQ(0, success);
+    }
+    sp<IGraphicBufferProducer> mProducer;
+    sp<IGraphicBufferConsumer> mConsumer;
+    sp<BufferItemConsumer> mItemConsumer;
+
+    sp<TestableSurface> mWindow;
+};
+
+TEST_F(ANativeWindowTest, getLastDequeueDuration_noDequeue_returnsZero) {
+    int result = ANativeWindow_getLastDequeueDuration(mWindow.get());
+    EXPECT_EQ(0, result);
+    EXPECT_EQ(0, mWindow->getLastDequeueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueDuration_withDequeue_returnsTime) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, result);
+
+    result = ANativeWindow_getLastDequeueDuration(mWindow.get());
+    EXPECT_GT(result, 0);
+    EXPECT_EQ(result, mWindow->getLastDequeueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noDequeue_returnsZero) {
+    int result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_EQ(0, result);
+    EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_noQueue_returnsZero) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, result);
+
+    result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_EQ(result, 0);
+    EXPECT_EQ(result, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastQueueDuration_withQueue_returnsTime) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, result);
+
+    result = ANativeWindow_queueBuffer(mWindow.get(), buffer, 0);
+
+    result = ANativeWindow_getLastQueueDuration(mWindow.get());
+    EXPECT_GT(result, 0);
+    EXPECT_EQ(result, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_noDequeue_returnsZero) {
+    int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+    EXPECT_EQ(0, result);
+    EXPECT_EQ(0, mWindow->getLastQueueDuration());
+}
+
+TEST_F(ANativeWindowTest, getLastDequeueStartTime_withDequeue_returnsTime) {
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, dequeueResult);
+
+    int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get());
+    EXPECT_GT(result, 0);
+    EXPECT_EQ(result, mWindow->getLastDequeueStartTime());
+}
+
+TEST_F(ANativeWindowTest, setDequeueTimeout_causesDequeueTimeout) {
+    nsecs_t timeout = milliseconds_to_nanoseconds(100);
+    int result = ANativeWindow_setDequeueTimeout(mWindow.get(), timeout);
+    EXPECT_EQ(0, result);
+
+    // The two dequeues should not timeout...
+    ANativeWindowBuffer* buffer;
+    int fd;
+    int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, dequeueResult);
+    int queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+    EXPECT_EQ(0, queueResult);
+    dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    close(fd);
+    EXPECT_EQ(0, dequeueResult);
+    queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1);
+    EXPECT_EQ(0, queueResult);
+
+    // ...but the third one should since the queue depth is too deep.
+    nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC);
+    dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd);
+    nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC);
+    close(fd);
+    EXPECT_EQ(TIMED_OUT, dequeueResult);
+    EXPECT_GE(end - start, timeout);
+}
diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp
index 20071be..cdb3d20 100644
--- a/libs/nativewindow/tests/Android.bp
+++ b/libs/nativewindow/tests/Android.bp
@@ -15,13 +15,22 @@
 //
 
 cc_test {
-    name: "AHardwareBufferTest",
+    name: "libnativewindow_test",
+    test_suites: [
+        "device-tests",
+    ],
     shared_libs: [
+        "libgui",
+        "liblog",
         "libnativewindow",
+        "libsync",
+        "libutils",
         "android.hardware.graphics.common@1.0",
     ],
     srcs: [
         "AHardwareBufferTest.cpp",
-        "c_compatibility.c"],
+        "ANativeWindowTest.cpp",
+        "c_compatibility.c",
+    ],
     cflags: ["-Wall", "-Werror"],
 }
diff --git a/libs/nativewindow/tests/c_compatibility.c b/libs/nativewindow/tests/c_compatibility.c
index befd88f..aa9b4f7 100644
--- a/libs/nativewindow/tests/c_compatibility.c
+++ b/libs/nativewindow/tests/c_compatibility.c
@@ -16,6 +16,7 @@
 
 #include <android/hardware_buffer.h>
 #include <android/native_window.h>
+#include <apex/window.h>
 #include <vndk/hardware_buffer.h>
 #include <vndk/window.h>
 
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index cc252d6..3d77059 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,9 +52,16 @@
         "gl/GLExtensions.cpp",
         "gl/GLFramebuffer.cpp",
         "gl/GLImage.cpp",
+        "gl/GLShadowTexture.cpp",
+        "gl/GLShadowVertexGenerator.cpp",
+        "gl/GLSkiaShadowPort.cpp",
         "gl/ImageManager.cpp",
         "gl/Program.cpp",
         "gl/ProgramCache.cpp",
+        "gl/filters/BlurFilter.cpp",
+        "gl/filters/KawaseBlurFilter.cpp",
+        "gl/filters/GaussianBlurFilter.cpp",
+        "gl/filters/GenericProgram.cpp",
     ],
 }
 
@@ -71,9 +78,6 @@
         "-fvisibility=hidden",
         "-Werror=format",
     ],
-    cppflags: [
-        "-fwhole-program-vtables", // requires ThinLTO
-    ],
     srcs: [
         ":librenderengine_sources",
         ":librenderengine_gl_sources",
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
index f5387f2..ed2f45f 100644
--- a/libs/renderengine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -21,38 +21,46 @@
 namespace android {
 namespace renderengine {
 
-Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize)
+Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+           size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize,
+           size_t indexCount)
       : mVertexCount(vertexCount),
         mVertexSize(vertexSize),
         mTexCoordsSize(texCoordSize),
-        mPrimitive(primitive) {
+        mCropCoordsSize(cropCoordsSize),
+        mShadowColorSize(shadowColorSize),
+        mShadowParamsSize(shadowParamsSize),
+        mPrimitive(primitive),
+        mIndexCount(indexCount) {
     if (vertexCount == 0) {
         mVertices.resize(1);
         mVertices[0] = 0.0f;
         mStride = 0;
         return;
     }
-
-    const size_t CROP_COORD_SIZE = 2;
-    size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
+    size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize;
     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, %zu)", vertexCount, vertexSize, texCoordSize,
-              CROP_COORD_SIZE);
+        ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize,
+              texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize);
         mVertices.resize(1);
         mVertices[0] = 0.0f;
         mVertexCount = 0;
         mVertexSize = 0;
         mTexCoordsSize = 0;
+        mCropCoordsSize = 0;
+        mShadowColorSize = 0;
+        mShadowParamsSize = 0;
         mStride = 0;
         return;
     }
 
     mVertices.resize(stride * vertexCount);
     mStride = stride;
+    mIndices.resize(indexCount);
 }
 
 Mesh::Primitive Mesh::getPrimitive() const {
@@ -80,6 +88,28 @@
     return mVertices.data() + mVertexSize + mTexCoordsSize;
 }
 
+float const* Mesh::getShadowColor() const {
+    return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+float* Mesh::getShadowColor() {
+    return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+
+float const* Mesh::getShadowParams() const {
+    return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+float* Mesh::getShadowParams() {
+    return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+
+uint16_t const* Mesh::getIndices() const {
+    return mIndices.data();
+}
+
+uint16_t* Mesh::getIndices() {
+    return mIndices.data();
+}
+
 size_t Mesh::getVertexCount() const {
     return mVertexCount;
 }
@@ -92,6 +122,14 @@
     return mTexCoordsSize;
 }
 
+size_t Mesh::getShadowColorSize() const {
+    return mShadowColorSize;
+}
+
+size_t Mesh::getShadowParamsSize() const {
+    return mShadowParamsSize;
+}
+
 size_t Mesh::getByteStride() const {
     return mStride * sizeof(float);
 }
@@ -100,5 +138,9 @@
     return mStride;
 }
 
+size_t Mesh::getIndexCount() const {
+    return mIndexCount;
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 166c267..0fdf093 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -24,23 +24,22 @@
 namespace android {
 namespace renderengine {
 
-std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags,
-                                                         uint32_t imageCacheSize) {
+std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
     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, imageCacheSize);
+        return renderengine::gl::GLESRenderEngine::create(args);
     }
     ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
-    return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize);
+    return renderengine::gl::GLESRenderEngine::create(args);
 }
 
 RenderEngine::~RenderEngine() = default;
 
 namespace impl {
 
-RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {}
+RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {}
 
 RenderEngine::~RenderEngine() = default;
 
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index d2a7525..e11b59f 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -46,8 +46,12 @@
 #include "GLExtensions.h"
 #include "GLFramebuffer.h"
 #include "GLImage.h"
+#include "GLShadowVertexGenerator.h"
 #include "Program.h"
 #include "ProgramCache.h"
+#include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
 
 extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
 
@@ -227,8 +231,7 @@
     return err;
 }
 
-std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags,
-                                                           uint32_t imageCacheSize) {
+std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCreationArgs& args) {
     // initialize EGL for the default display
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
     if (!eglInitialize(display, nullptr, nullptr)) {
@@ -243,14 +246,13 @@
     // supported.
     EGLConfig config = EGL_NO_CONFIG;
     if (!extensions.hasNoConfigContext()) {
-        config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+        config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true);
     }
 
-    bool useContextPriority = extensions.hasContextPriority() &&
-            (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
+    bool useContextPriority =
+            extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH;
     EGLContext protectedContext = EGL_NO_CONTEXT;
-    if ((featureFlags & RenderEngine::ENABLE_PROTECTED_CONTEXT) &&
-        extensions.hasProtectedContent()) {
+    if (args.enableProtectedContext && extensions.hasProtectedContent()) {
         protectedContext = createEglContext(display, config, nullptr, useContextPriority,
                                             Protection::PROTECTED);
         ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context");
@@ -264,7 +266,8 @@
 
     EGLSurface dummy = EGL_NO_SURFACE;
     if (!extensions.hasSurfacelessContext()) {
-        dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED);
+        dummy = createDummyEglPbufferSurface(display, config, args.pixelFormat,
+                                             Protection::UNPROTECTED);
         LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
     }
     EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
@@ -274,14 +277,17 @@
 
     EGLSurface protectedDummy = EGL_NO_SURFACE;
     if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) {
-        protectedDummy =
-                createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED);
+        protectedDummy = createDummyEglPbufferSurface(display, config, args.pixelFormat,
+                                                      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());
 
+    LOG_ALWAYS_FATAL_IF(args.supportsBackgroundBlur && version < GLES_VERSION_3_0,
+        "Blurs require OpenGL ES 3.0. Please unset ro.surface_flinger.supports_background_blur");
+
     // initialize the renderer while GL is current
     std::unique_ptr<GLESRenderEngine> engine;
     switch (version) {
@@ -291,9 +297,8 @@
             break;
         case GLES_VERSION_2_0:
         case GLES_VERSION_3_0:
-            engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy,
-                                                        protectedContext, protectedDummy,
-                                                        imageCacheSize);
+            engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, dummy,
+                                                        protectedContext, protectedDummy);
             break;
     }
 
@@ -347,10 +352,10 @@
     return config;
 }
 
-GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config,
-                                   EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
-                                   EGLSurface protectedDummy, uint32_t imageCacheSize)
-      : renderengine::impl::RenderEngine(featureFlags),
+GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display,
+                                   EGLConfig config, EGLContext ctxt, EGLSurface dummy,
+                                   EGLContext protectedContext, EGLSurface protectedDummy)
+      : renderengine::impl::RenderEngine(args),
         mEGLDisplay(display),
         mEGLConfig(config),
         mEGLContext(ctxt),
@@ -359,8 +364,8 @@
         mProtectedDummySurface(protectedDummy),
         mVpWidth(0),
         mVpHeight(0),
-        mFramebufferImageCacheSize(imageCacheSize),
-        mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) {
+        mFramebufferImageCacheSize(args.imageCacheSize),
+        mUseColorManagement(args.useColorManagement) {
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
 
@@ -423,6 +428,18 @@
         mTraceGpuCompletion = true;
         mFlushTracer = std::make_unique<FlushTracer>(this);
     }
+
+    if (args.supportsBackgroundBlur) {
+        char isGaussian[PROPERTY_VALUE_MAX];
+        property_get("debug.sf.gaussianBlur", isGaussian, "0");
+        if (atoi(isGaussian)) {
+            mBlurFilter = new GaussianBlurFilter(*this);
+        } else {
+            mBlurFilter = new KawaseBlurFilter(*this);
+        }
+        checkErrors("BlurFilter creation");
+    }
+
     mImageManager = std::make_unique<ImageManager>(this);
     mDrawingBuffer = createFramebuffer();
 }
@@ -458,11 +475,8 @@
 
 void GLESRenderEngine::primeCache() const {
     ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
-                                           mFeatureFlags & USE_COLOR_MANAGEMENT);
-}
-
-bool GLESRenderEngine::isCurrent() const {
-    return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
+                                           mArgs.useColorManagement,
+                                           mArgs.precacheToneMapperShaderOnly);
 }
 
 base::unique_fd GLESRenderEngine::flush() {
@@ -571,7 +585,10 @@
                                            float alpha) {
     size_t c;
     Rect const* r = region.getArray(&c);
-    Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
+    Mesh mesh = Mesh::Builder()
+                        .setPrimitive(Mesh::TRIANGLES)
+                        .setVertices(c * 6 /* count */, 2 /* size */)
+                        .build();
     Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
     for (size_t i = 0; i < c; i++, r++) {
         position[i * 6 + 0].x = r->left;
@@ -831,11 +848,14 @@
     drawMesh(mesh);
 
     // The middle part of the layer can turn off blending.
-    const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius);
-    setScissor(middleRect);
-    mState.cornerRadius = 0.0;
-    disableBlending();
-    drawMesh(mesh);
+    if (topRect.bottom < bottomRect.top) {
+        const Rect middleRect(bounds.left, bounds.top + radius, bounds.right,
+                              bounds.bottom - radius);
+        setScissor(middleRect);
+        mState.cornerRadius = 0.0;
+        disableBlending();
+        drawMesh(mesh);
+    }
     disableScissor();
 }
 
@@ -855,7 +875,6 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
 
     uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-
     ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
              glStatus);
 
@@ -870,11 +889,19 @@
 }
 
 void GLESRenderEngine::checkErrors() const {
+    checkErrors(nullptr);
+}
+
+void GLESRenderEngine::checkErrors(const char* tag) 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));
+        if (tag == nullptr) {
+            ALOGE("GL error 0x%04x", int(error));
+        } else {
+            ALOGE("GL error: %s -> 0x%04x", tag, int(error));
+        }
     } while (true);
 }
 
@@ -936,7 +963,7 @@
 }
 
 status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
-                                      const std::vector<LayerSettings>& layers,
+                                      const std::vector<const LayerSettings*>& layers,
                                       ANativeWindowBuffer* const buffer,
                                       const bool useFramebufferCache, base::unique_fd&& bufferFence,
                                       base::unique_fd* drawFence) {
@@ -956,13 +983,38 @@
         return BAD_VALUE;
     }
 
-    BindNativeBufferAsFramebuffer fbo(*this, buffer, useFramebufferCache);
+    std::unique_ptr<BindNativeBufferAsFramebuffer> fbo;
+    // Gathering layers that requested blur, we'll need them to decide when to render to an
+    // offscreen buffer, and when to render to the native buffer.
+    std::deque<const LayerSettings*> blurLayers;
+    if (CC_LIKELY(mBlurFilter != nullptr)) {
+        for (auto layer : layers) {
+            if (layer->backgroundBlurRadius > 0) {
+                blurLayers.push_back(layer);
+            }
+        }
+    }
+    const auto blurLayersSize = blurLayers.size();
 
-    if (fbo.getStatus() != NO_ERROR) {
-        ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
-              buffer->handle);
-        checkErrors();
-        return fbo.getStatus();
+    if (blurLayersSize == 0) {
+        fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache);
+        if (fbo->getStatus() != NO_ERROR) {
+            ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+                  buffer->handle);
+            checkErrors();
+            return fbo->getStatus();
+        }
+        setViewportAndProjection(display.physicalDisplay, display.clip);
+    } else {
+        setViewportAndProjection(display.physicalDisplay, display.clip);
+        auto status =
+                mBlurFilter->setAsDrawTarget(display, blurLayers.front()->backgroundBlurRadius);
+        if (status != NO_ERROR) {
+            ALOGE("Failed to prepare blur filter! Aborting GPU composition for buffer (%p).",
+                  buffer->handle);
+            checkErrors();
+            return status;
+        }
     }
 
     // clear the entire buffer, sometimes when we reuse buffers we'd persist
@@ -972,8 +1024,6 @@
     // opaque layers.
     clearWithColor(0.0, 0.0, 0.0, 0.0);
 
-    setViewportAndProjection(display.physicalDisplay, display.clip);
-
     setOutputDataSpace(display.outputDataspace);
     setDisplayMaxLuminance(display.maxLuminance);
 
@@ -984,41 +1034,86 @@
         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;
+    Mesh mesh = Mesh::Builder()
+                        .setPrimitive(Mesh::TRIANGLE_FAN)
+                        .setVertices(4 /* count */, 2 /* size */)
+                        .setTexCoords(2 /* size */)
+                        .setCropCoords(2 /* size */)
+                        .build();
+    for (auto const layer : layers) {
+        if (blurLayers.size() > 0 && blurLayers.front() == layer) {
+            blurLayers.pop_front();
 
-        const FloatRect bounds = layer.geometry.boundaries;
+            auto status = mBlurFilter->prepare();
+            if (status != NO_ERROR) {
+                ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+                      buffer->handle);
+                checkErrors("Can't render first blur pass");
+                return status;
+            }
+
+            if (blurLayers.size() == 0) {
+                // Done blurring, time to bind the native FBO and render our blur onto it.
+                fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer,
+                                                                      useFramebufferCache);
+                status = fbo->getStatus();
+                setViewportAndProjection(display.physicalDisplay, display.clip);
+            } else {
+                // There's still something else to blur, so let's keep rendering to our FBO
+                // instead of to the display.
+                status = mBlurFilter->setAsDrawTarget(display,
+                                                      blurLayers.front()->backgroundBlurRadius);
+            }
+            if (status != NO_ERROR) {
+                ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).",
+                      buffer->handle);
+                checkErrors("Can't bind native framebuffer");
+                return status;
+            }
+
+            status = mBlurFilter->render(blurLayersSize > 1);
+            if (status != NO_ERROR) {
+                ALOGE("Failed to render blur effect! Aborting GPU composition for buffer (%p).",
+                      buffer->handle);
+                checkErrors("Can't render blur filter");
+                return status;
+            }
+        }
+
+        mState.maxMasteringLuminance = layer->source.buffer.maxMasteringLuminance;
+        mState.maxContentLuminance = layer->source.buffer.maxContentLuminance;
+        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);
+        setupLayerCropping(*layer, mesh);
+        setColorTransform(display.colorTransform * layer->colorTransform);
 
         bool usePremultipliedAlpha = true;
         bool disableTexture = true;
         bool isOpaque = false;
-
-        if (layer.source.buffer.buffer != nullptr) {
+        if (layer->source.buffer.buffer != nullptr) {
             disableTexture = false;
-            isOpaque = layer.source.buffer.isOpaque;
+            isOpaque = layer->source.buffer.isOpaque;
 
-            sp<GraphicBuffer> gBuf = layer.source.buffer.buffer;
-            bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf,
-                                      layer.source.buffer.fence);
+            sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
+            bindExternalTextureBuffer(layer->source.buffer.textureName, gBuf,
+                                      layer->source.buffer.fence);
 
-            usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha;
-            Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName);
-            mat4 texMatrix = layer.source.buffer.textureTransform;
+            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.setFiltering(layer->source.buffer.useTextureFiltering);
 
             texture.setDimensions(gBuf->getWidth(), gBuf->getHeight());
-            setSourceY410BT2020(layer.source.buffer.isY410BT2020);
+            setSourceY410BT2020(layer->source.buffer.isY410BT2020);
 
             renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
             texCoords[0] = vec2(0.0, 0.0);
@@ -1028,28 +1123,32 @@
             setupLayerTexturing(texture);
         }
 
-        const half3 solidColor = layer.source.solidColor;
-        const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha);
+        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) {
+                           layer->geometry.roundedCornersRadius);
+        if (layer->disableBlending) {
             glDisable(GL_BLEND);
         }
-        setSourceDataSpace(layer.sourceDataspace);
+        setSourceDataSpace(layer->sourceDataspace);
 
+        if (layer->shadow.length > 0.0f) {
+            handleShadow(layer->geometry.boundaries, layer->geometry.roundedCornersRadius,
+                         layer->shadow);
+        }
         // We only want to do a special handling for rounded corners when having rounded corners
         // is the only reason it needs to turn on blending, otherwise, we handle it like the
         // usual way since it needs to turn on blending anyway.
-        if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
-            handleRoundedCorners(display, layer, mesh);
+        else if (layer->geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+            handleRoundedCorners(display, *layer, mesh);
         } else {
             drawMesh(mesh);
         }
 
         // Cleanup if there's a buffer source
-        if (layer.source.buffer.buffer != nullptr) {
+        if (layer->source.buffer.buffer != nullptr) {
             disableBlending();
             setSourceY410BT2020(false);
             disableTexturing();
@@ -1076,33 +1175,6 @@
     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();
@@ -1166,14 +1238,6 @@
     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;
 }
@@ -1215,13 +1279,23 @@
                               mesh.getByteStride(), mesh.getCropCoords());
     }
 
+    if (mState.drawShadows) {
+        glEnableVertexAttribArray(Program::shadowColor);
+        glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE,
+                              mesh.getByteStride(), mesh.getShadowColor());
+
+        glEnableVertexAttribArray(Program::shadowParams);
+        glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE,
+                              mesh.getByteStride(), mesh.getShadowParams());
+    }
+
+    Description managedState = mState;
     // 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 =
@@ -1312,27 +1386,25 @@
             managedState.outputTransferFunction =
                     Description::dataSpaceToTransferFunction(outputTransfer);
         }
+    }
 
-        ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
-                                                                   : mEGLContext,
-                                               managedState);
+    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);
-        }
+    if (mState.drawShadows) {
+        glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
+                       mesh.getIndices());
     } else {
-        ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
-                                                                   : mEGLContext,
-                                               mState);
-
         glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
     }
 
+    if (mUseColorManagement && outputDebugPPMs) {
+        static uint64_t managedColorFrameCount = 0;
+        std::ostringstream out;
+        out << "/data/texture_out" << managedColorFrameCount++;
+        writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+    }
+
     if (mesh.getTexCoordsSize()) {
         glDisableVertexAttribArray(Program::texCoords);
     }
@@ -1340,6 +1412,11 @@
     if (mState.cornerRadius > 0.0f) {
         glDisableVertexAttribArray(Program::cropCoords);
     }
+
+    if (mState.drawShadows) {
+        glDisableVertexAttribArray(Program::shadowColor);
+        glDisableVertexAttribArray(Program::shadowParams);
+    }
 }
 
 size_t GLESRenderEngine::getMaxTextureSize() const {
@@ -1574,6 +1651,36 @@
     }
 }
 
+void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+                                    const ShadowSettings& settings) {
+    ATRACE_CALL();
+    const float casterZ = settings.length / 2.0f;
+    const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
+                                          settings.casterIsTranslucent, settings.ambientColor,
+                                          settings.spotColor, settings.lightPos,
+                                          settings.lightRadius);
+
+    // setup mesh for both shadows
+    Mesh mesh = Mesh::Builder()
+                        .setPrimitive(Mesh::TRIANGLES)
+                        .setVertices(shadows.getVertexCount(), 2 /* size */)
+                        .setShadowAttrs()
+                        .setIndices(shadows.getIndexCount())
+                        .build();
+
+    Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
+    Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
+    Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
+    shadows.fillVertices(position, shadowColor, shadowParams);
+    shadows.fillIndices(mesh.getIndicesArray());
+
+    mState.cornerRadius = 0.0f;
+    mState.drawShadows = true;
+    setupLayerTexturing(mShadowTexture.getTexture());
+    drawMesh(mesh);
+    mState.drawShadows = false;
+}
+
 } // namespace gl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index dd60e50..ebf78fe 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -32,6 +32,7 @@
 #include <renderengine/RenderEngine.h>
 #include <renderengine/private/Description.h>
 #include <sys/types.h>
+#include "GLShadowTexture.h"
 #include "ImageManager.h"
 
 #define EGL_NO_CONFIG ((EGLConfig)0)
@@ -46,30 +47,18 @@
 namespace gl {
 
 class GLImage;
+class BlurFilter;
 
 class GLESRenderEngine : public impl::RenderEngine {
 public:
-    static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags,
-                                                    uint32_t imageCacheSize);
-    static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+    static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args);
 
-    GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag
-                     EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
-                     EGLContext protectedContext, EGLSurface protectedDummy,
-                     uint32_t imageCacheSize);
+    GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config,
+                     EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext,
+                     EGLSurface protectedDummy);
     ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
 
-    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 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;
@@ -79,18 +68,16 @@
     void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex);
     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,
+    status_t drawLayers(const DisplaySettings& display,
+                        const std::vector<const LayerSettings*>& layers,
                         ANativeWindowBuffer* buffer, const bool useFramebufferCache,
                         base::unique_fd&& bufferFence, base::unique_fd* drawFence) override;
 
-    // internal to RenderEngine
     EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
-    EGLConfig getEGLConfig() const { return mEGLConfig; }
     // Creates an output image for rendering to
     EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected,
                                                bool useFramebufferCache)
@@ -112,27 +99,6 @@
     Framebuffer* getFramebufferForDrawing() override;
     void dump(std::string& result) override EXCLUDES(mRenderingMutex)
             EXCLUDES(mFramebufferImageCacheMutex);
-    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;
 
@@ -144,12 +110,17 @@
         GLES_VERSION_3_0 = 0x30000,
     };
 
+    static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
     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);
+    std::unique_ptr<Framebuffer> createFramebuffer();
+    std::unique_ptr<Image> createImage();
+    void checkErrors() const;
+    void checkErrors(const char* tag) const;
     void setScissor(const Rect& region);
     void disableScissor();
     bool waitSync(EGLSyncKHR sync, EGLint flags);
@@ -176,6 +147,30 @@
     // blending is an expensive operation, we want to turn off blending when it's not necessary.
     void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer,
                               const Mesh& mesh);
+    base::unique_fd flush();
+    bool finish();
+    bool waitFence(base::unique_fd fenceFd);
+    void clearWithColor(float red, float green, float blue, float alpha);
+    void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
+    void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+                      const ShadowSettings& shadowSettings);
+    void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+                            const half4& color, float cornerRadius);
+    void setupLayerTexturing(const Texture& texture);
+    void setupFillWithColor(float r, float g, float b, float a);
+    void setColorTransform(const mat4& colorTransform);
+    void disableTexturing();
+    void disableBlending();
+    void setupCornerRadiusCropSize(float width, float height);
+
+    // HDR and color management related functions and state
+    void setSourceY410BT2020(bool enable);
+    void setSourceDataSpace(ui::Dataspace source);
+    void setOutputDataSpace(ui::Dataspace dataspace);
+    void setDisplayMaxLuminance(const float maxLuminance);
+
+    // drawing
+    void drawMesh(const Mesh& mesh);
 
     EGLDisplay mEGLDisplay;
     EGLConfig mEGLConfig;
@@ -189,6 +184,7 @@
     GLuint mVpWidth;
     GLuint mVpHeight;
     Description mState;
+    GLShadowTexture mShadowTexture;
 
     mat4 mSrgbToXyz;
     mat4 mDisplayP3ToXyz;
@@ -237,6 +233,9 @@
 
     std::unique_ptr<Framebuffer> mDrawingBuffer;
 
+    // Blur effect processor, only instantiated when a layer requests it.
+    BlurFilter* mBlurFilter = nullptr;
+
     class FlushTracer {
     public:
         FlushTracer(GLESRenderEngine* engine);
@@ -260,6 +259,11 @@
     };
     friend class FlushTracer;
     friend class ImageManager;
+    friend class GLFramebuffer;
+    friend class BlurFilter;
+    friend class GaussianBlurFilter;
+    friend class KawaseBlurFilter;
+    friend class GenericProgram;
     std::unique_ptr<FlushTracer> mFlushTracer;
     std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
 };
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 5fbb5ba..153935b 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -20,8 +20,8 @@
 
 #include <GLES/gl.h>
 #include <GLES/glext.h>
-#include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
 #include <gui/DebugEGLImageTracker.h>
 #include <nativebase/nativebase.h>
 #include <utils/Trace.h>
@@ -32,14 +32,23 @@
 namespace gl {
 
 GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
+      : GLFramebuffer(engine, false /* multiTarget */) {}
+
+GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine, bool multiTarget)
       : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
     glGenTextures(1, &mTextureName);
+    if (multiTarget) {
+        glGenTextures(1, &mSecondaryTextureName);
+    }
     glGenFramebuffers(1, &mFramebufferName);
 }
 
 GLFramebuffer::~GLFramebuffer() {
     glDeleteFramebuffers(1, &mFramebufferName);
     glDeleteTextures(1, &mTextureName);
+    if (mSecondaryTextureName != -1) {
+        glDeleteTextures(1, &mSecondaryTextureName);
+    }
 }
 
 bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
@@ -68,6 +77,63 @@
     return true;
 }
 
+void GLFramebuffer::allocateBuffers(uint32_t width, uint32_t height) {
+    ATRACE_CALL();
+
+    glBindTexture(GL_TEXTURE_2D, mTextureName);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+
+    const bool multiTarget = mSecondaryTextureName != -1;
+    if (multiTarget) {
+        glBindTexture(GL_TEXTURE_2D, mSecondaryTextureName);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+    }
+
+    mBufferHeight = height;
+    mBufferWidth = width;
+    mEngine.checkErrors("Allocating Fbo texture");
+
+    bind();
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
+    if (multiTarget) {
+        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
+                               mSecondaryTextureName, 0);
+        GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
+        glDrawBuffers(2, buffers);
+    }
+    mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    unbind();
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    if (mStatus != GL_FRAMEBUFFER_COMPLETE) {
+        ALOGE("Frame buffer is not complete. Error %d", mStatus);
+    }
+}
+
+void GLFramebuffer::bind() const {
+    glBindFramebuffer(GL_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsReadBuffer() const {
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::bindAsDrawBuffer() const {
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferName);
+}
+
+void GLFramebuffer::unbind() const {
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
 } // namespace gl
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index b7650bb..69102d6 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -20,6 +20,7 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
+#include <GLES2/gl2.h>
 #include <renderengine/Framebuffer.h>
 
 struct ANativeWindowBuffer;
@@ -33,22 +34,32 @@
 class GLFramebuffer : public renderengine::Framebuffer {
 public:
     explicit GLFramebuffer(GLESRenderEngine& engine);
+    explicit GLFramebuffer(GLESRenderEngine& engine, bool multiTarget);
     ~GLFramebuffer() override;
 
     bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
                                const bool useFramebufferCache) override;
+    void allocateBuffers(uint32_t width, uint32_t height);
     EGLImageKHR getEGLImage() const { return mEGLImage; }
     uint32_t getTextureName() const { return mTextureName; }
+    uint32_t getSecondaryTextureName() const { return mSecondaryTextureName; }
     uint32_t getFramebufferName() const { return mFramebufferName; }
     int32_t getBufferHeight() const { return mBufferHeight; }
     int32_t getBufferWidth() const { return mBufferWidth; }
+    GLenum getStatus() const { return mStatus; }
+    void bind() const;
+    void bindAsReadBuffer() const;
+    void bindAsDrawBuffer() const;
+    void unbind() const;
 
 private:
     GLESRenderEngine& mEngine;
     EGLDisplay mEGLDisplay;
     EGLImageKHR mEGLImage;
     bool usingFramebufferCache = false;
+    GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
     uint32_t mTextureName, mFramebufferName;
+    uint32_t mSecondaryTextureName = -1;
 
     int32_t mBufferHeight = 0;
     int32_t mBufferWidth = 0;
diff --git a/libs/renderengine/gl/GLShadowTexture.cpp b/libs/renderengine/gl/GLShadowTexture.cpp
new file mode 100644
index 0000000..2423a34
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
+
+#include "GLShadowTexture.h"
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowTexture::GLShadowTexture() {
+    fillShadowTextureData(mTextureData, SHADOW_TEXTURE_WIDTH);
+
+    glGenTextures(1, &mName);
+    glBindTexture(GL_TEXTURE_2D, mName);
+    glTexImage2D(GL_TEXTURE_2D, 0 /* base image level */, GL_ALPHA, SHADOW_TEXTURE_WIDTH,
+                 SHADOW_TEXTURE_HEIGHT, 0 /* border */, GL_ALPHA, GL_UNSIGNED_BYTE, mTextureData);
+    mTexture.init(Texture::TEXTURE_2D, mName);
+    mTexture.setFiltering(true);
+    mTexture.setDimensions(SHADOW_TEXTURE_WIDTH, 1);
+}
+
+GLShadowTexture::~GLShadowTexture() {
+    glDeleteTextures(1, &mName);
+}
+
+const Texture& GLShadowTexture::getTexture() {
+    return mTexture;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowTexture.h b/libs/renderengine/gl/GLShadowTexture.h
new file mode 100644
index 0000000..250a9d7
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowTexture.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <renderengine/Texture.h>
+#include <cstdint>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLShadowTexture {
+public:
+    GLShadowTexture();
+    ~GLShadowTexture();
+
+    const Texture& getTexture();
+
+private:
+    static constexpr int SHADOW_TEXTURE_WIDTH = 128;
+    static constexpr int SHADOW_TEXTURE_HEIGHT = 1;
+
+    GLuint mName;
+    Texture mTexture;
+    uint8_t mTextureData[SHADOW_TEXTURE_WIDTH];
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
new file mode 100644
index 0000000..3181f9b
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 <renderengine/Mesh.h>
+
+#include <math/vec4.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include "GLShadowVertexGenerator.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
+                                                 float casterCornerRadius, float casterZ,
+                                                 bool casterIsTranslucent, const vec4& ambientColor,
+                                                 const vec4& spotColor, const vec3& lightPosition,
+                                                 float lightRadius) {
+    mDrawAmbientShadow = ambientColor.a > 0.f;
+    mDrawSpotShadow = spotColor.a > 0.f;
+
+    // Generate geometries and find number of vertices to generate
+    if (mDrawAmbientShadow) {
+        mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
+                                                          casterIsTranslucent, ambientColor);
+        mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
+        mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
+    } else {
+        mAmbientShadowVertexCount = 0;
+        mAmbientShadowIndexCount = 0;
+    }
+
+    if (mDrawSpotShadow) {
+        mSpotShadowGeometry =
+                getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
+                                      spotColor, lightPosition, lightRadius);
+        mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
+        mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
+    } else {
+        mSpotShadowVertexCount = 0;
+        mSpotShadowIndexCount = 0;
+    }
+}
+
+size_t GLShadowVertexGenerator::getVertexCount() const {
+    return mAmbientShadowVertexCount + mSpotShadowVertexCount;
+}
+
+size_t GLShadowVertexGenerator::getIndexCount() const {
+    return mAmbientShadowIndexCount + mSpotShadowIndexCount;
+}
+
+void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
+                                           Mesh::VertexArray<vec4>& color,
+                                           Mesh::VertexArray<vec3>& params) const {
+    if (mDrawAmbientShadow) {
+        fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
+                                color, params);
+    }
+    if (mDrawSpotShadow) {
+        fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
+                                Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
+                                Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
+                                Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
+    }
+}
+
+void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
+    if (mDrawAmbientShadow) {
+        fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
+                               0 /* starting vertex offset */, indices);
+    }
+    if (mDrawSpotShadow) {
+        fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
+                               mAmbientShadowVertexCount /* starting vertex offset */,
+                               &(indices[mAmbientShadowIndexCount]));
+    }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h
new file mode 100644
index 0000000..112f976
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.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.
+ */
+
+#pragma once
+
+#include <math/vec4.h>
+#include <ui/Rect.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * Generates gl attributes required to draw shadow spot and/or ambient shadows.
+ *
+ * Each shadow can support different colors. This class generates three vertex attributes for
+ * each shadow, its position, color and shadow params(offset and distance). These can be sent
+ * using a single glDrawElements call.
+ */
+class GLShadowVertexGenerator {
+public:
+    GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
+                            bool casterIsTranslucent, const vec4& ambientColor,
+                            const vec4& spotColor, const vec3& lightPosition, float lightRadius);
+    ~GLShadowVertexGenerator() = default;
+
+    size_t getVertexCount() const;
+    size_t getIndexCount() const;
+    void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
+                      Mesh::VertexArray<vec3>& params) const;
+    void fillIndices(uint16_t* indices) const;
+
+private:
+    bool mDrawAmbientShadow;
+    std::unique_ptr<Geometry> mAmbientShadowGeometry;
+    int mAmbientShadowVertexCount = 0;
+    int mAmbientShadowIndexCount = 0;
+
+    bool mDrawSpotShadow;
+    std::unique_ptr<Geometry> mSpotShadowGeometry;
+    int mSpotShadowVertexCount = 0;
+    int mSpotShadowIndexCount = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
new file mode 100644
index 0000000..da8b435
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.cpp
@@ -0,0 +1,656 @@
+/*
+ * 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 <math/vec4.h>
+
+#include <renderengine/Mesh.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include <utils/Log.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ *   coordinate space and will be transformed by the vertex shader.
+ */
+
+static inline float divide_and_pin(float numer, float denom, float min, float max) {
+    if (denom == 0.0f) return min;
+    return std::clamp(numer / denom, min, max);
+}
+
+static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
+static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
+static constexpr auto kAmbientGeomFactor = 64.0f;
+// Assuming that we have a light height of 600 for the spot shadow,
+// the spot values will reach their maximum at a height of approximately 292.3077.
+// We'll round up to 300 to keep it simple.
+static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
+
+inline float AmbientBlurRadius(float height) {
+    return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
+}
+inline float AmbientRecipAlpha(float height) {
+    return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Data
+//
+// We have two possible cases for geometry for a circle:
+
+// In the case of a normal fill, we draw geometry for the circle as an octagon.
+static const uint16_t gFillCircleIndices[] = {
+        // enter the octagon
+        // clang-format off
+         0, 1, 8, 1, 2, 8,
+         2, 3, 8, 3, 4, 8,
+         4, 5, 8, 5, 6, 8,
+         6, 7, 8, 7, 0, 8,
+        // clang-format on
+};
+
+// For stroked circles, we use two nested octagons.
+static const uint16_t gStrokeCircleIndices[] = {
+        // enter the octagon
+        // clang-format off
+         0, 1,  9, 0,  9,  8,
+         1, 2, 10, 1, 10,  9,
+         2, 3, 11, 2, 11, 10,
+         3, 4, 12, 3, 12, 11,
+         4, 5, 13, 4, 13, 12,
+         5, 6, 14, 5, 14, 13,
+         6, 7, 15, 6, 15, 14,
+         7, 0,  8, 7,  8, 15,
+        // clang-format on
+};
+
+#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
+static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
+static const int kVertsPerStrokeCircle = 16;
+static const int kVertsPerFillCircle = 9;
+
+static int circle_type_to_vert_count(bool stroked) {
+    return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
+}
+
+static int circle_type_to_index_count(bool stroked) {
+    return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
+}
+
+static const uint16_t* circle_type_to_indices(bool stroked) {
+    return stroked ? gStrokeCircleIndices : gFillCircleIndices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RoundRect Data
+//
+// The geometry for a shadow roundrect is similar to a 9-patch:
+//    ____________
+//   |_|________|_|
+//   | |        | |
+//   | |        | |
+//   | |        | |
+//   |_|________|_|
+//   |_|________|_|
+//
+// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
+// shows the upper part of the upper left corner. The bottom triangle would similarly be split
+// into two triangles.)
+//    ________
+//   |\  \   |
+//   |  \ \  |
+//   |    \\ |
+//   |      \|
+//   --------
+//
+// The center of the fan handles the curve of the corner. For roundrects where the stroke width
+// is greater than the corner radius, the outer triangles blend from the curve to the straight
+// sides. Otherwise these triangles will be degenerate.
+//
+// In the case where the stroke width is greater than the corner radius and the
+// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
+// This rectangle extends the coverage values of the center edges of the 9-patch.
+//    ____________
+//   |_|________|_|
+//   | |\ ____ /| |
+//   | | |    | | |
+//   | | |____| | |
+//   |_|/______\|_|
+//   |_|________|_|
+//
+// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
+
+static const uint16_t gRRectIndices[] = {
+        // clang-format off
+     // overstroke quads
+     // we place this at the beginning so that we can skip these indices when rendering as filled
+     0, 6, 25, 0, 25, 24,
+     6, 18, 27, 6, 27, 25,
+     18, 12, 26, 18, 26, 27,
+     12, 0, 24, 12, 24, 26,
+
+     // corners
+     0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
+     6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
+     12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
+     18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
+
+     // edges
+     0, 5, 11, 0, 11, 6,
+     6, 7, 19, 6, 19, 18,
+     18, 23, 17, 18, 17, 12,
+     12, 13, 1, 12, 1, 0,
+
+     // fill quad
+     // we place this at the end so that we can skip these indices when rendering as stroked
+     0, 6, 18, 0, 18, 12,
+        // clang-format on
+};
+
+// overstroke count
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
+// simple stroke count skips overstroke indices
+static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
+// fill count adds final quad to stroke count
+static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
+static const int kVertsPerStrokeRRect = 24;
+static const int kVertsPerOverstrokeRRect = 28;
+static const int kVertsPerFillRRect = 24;
+
+static int rrect_type_to_vert_count(RRectType type) {
+    switch (type) {
+        case kFill_RRectType:
+            return kVertsPerFillRRect;
+        case kStroke_RRectType:
+            return kVertsPerStrokeRRect;
+        case kOverstroke_RRectType:
+            return kVertsPerOverstrokeRRect;
+    }
+    ALOGE("Invalid rect type: %d", type);
+    return -1;
+}
+
+static int rrect_type_to_index_count(RRectType type) {
+    switch (type) {
+        case kFill_RRectType:
+            return kIndicesPerFillRRect;
+        case kStroke_RRectType:
+            return kIndicesPerStrokeRRect;
+        case kOverstroke_RRectType:
+            return kIndicesPerOverstrokeRRect;
+    }
+    ALOGE("Invalid rect type: %d", type);
+    return -1;
+}
+
+static const uint16_t* rrect_type_to_indices(RRectType type) {
+    switch (type) {
+        case kFill_RRectType:
+        case kStroke_RRectType:
+            return gRRectIndices + 6 * 4;
+        case kOverstroke_RRectType:
+            return gRRectIndices;
+    }
+    ALOGE("Invalid rect type: %d", type);
+    return nullptr;
+}
+
+static void fillInCircleVerts(const Geometry& args, bool isStroked,
+                              Mesh::VertexArray<vec2>& position,
+                              Mesh::VertexArray<vec4>& shadowColor,
+                              Mesh::VertexArray<vec3>& shadowParams) {
+    vec4 color = args.fColor;
+    float outerRadius = args.fOuterRadius;
+    float innerRadius = args.fInnerRadius;
+    float blurRadius = args.fBlurRadius;
+    float distanceCorrection = outerRadius / blurRadius;
+
+    const FloatRect& bounds = args.fDevBounds;
+
+    // The inner radius in the vertex data must be specified in normalized space.
+    innerRadius = innerRadius / outerRadius;
+
+    vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
+    float halfWidth = 0.5f * bounds.getWidth();
+    float octOffset = 0.41421356237f; // sqrt(2) - 1
+    int vertexCount = 0;
+
+    position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
+    vertexCount++;
+
+    position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
+    shadowColor[vertexCount] = color;
+    shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
+    vertexCount++;
+
+    if (isStroked) {
+        // compute the inner ring
+
+        // cosine and sine of pi/8
+        float c = 0.923579533f;
+        float s = 0.382683432f;
+        float r = args.fInnerRadius;
+
+        position[vertexCount] = center + vec2(-s * r, -c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(s * r, -c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(c * r, -s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(c * r, s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(s * r, c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(-s * r, c * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(-c * r, s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = center + vec2(-c * r, -s * r);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
+        vertexCount++;
+    } else {
+        // filled
+        position[vertexCount] = center;
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+    }
+}
+
+static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
+                             Mesh::VertexArray<vec4>& shadowColor,
+                             Mesh::VertexArray<vec3>& shadowParams) {
+    vec4 color = args.fColor;
+    float outerRadius = args.fOuterRadius;
+
+    const FloatRect& bounds = args.fDevBounds;
+
+    float umbraInset = args.fUmbraInset;
+    float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
+    if (umbraInset > minDim) {
+        umbraInset = minDim;
+    }
+
+    float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
+                       bounds.left + umbraInset, bounds.right - umbraInset};
+    float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
+                     bounds.left + outerRadius, bounds.right - outerRadius};
+    float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
+    float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
+                       bounds.bottom - umbraInset};
+    float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
+                     bounds.bottom - outerRadius, bounds.bottom - outerRadius};
+    float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
+
+    float blurRadius = args.fBlurRadius;
+
+    // In the case where we have to inset more for the umbra, our two triangles in the
+    // corner get skewed to a diamond rather than a square. To correct for that,
+    // we also skew the vectors we send to the shader that help define the circle.
+    // By doing so, we end up with a quarter circle in the corner rather than the
+    // elliptical curve.
+
+    // This is a bit magical, but it gives us the correct results at extrema:
+    //   a) umbraInset == outerRadius produces an orthogonal vector
+    //   b) outerRadius == 0 produces a diagonal vector
+    // And visually the corner looks correct.
+    vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
+    outerVec = normalize(outerVec);
+    // We want the circle edge to fall fractionally along the diagonal at
+    //      (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
+    //
+    // Setting the components of the diagonal offset to the following value will give us that.
+    float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
+    vec2 diagVec = vec2(diagVal, diagVal);
+    float distanceCorrection = umbraInset / blurRadius;
+
+    int vertexCount = 0;
+    // build corner by corner
+    for (int i = 0; i < 4; ++i) {
+        // inner point
+        position[vertexCount] = vec2(xInner[i], yInner[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // outer points
+        position[vertexCount] = vec2(xOuter[i], yInner[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xOuter[i], yMid[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xOuter[i], yOuter[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xMid[i], yOuter[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+        vertexCount++;
+
+        position[vertexCount] = vec2(xInner[i], yOuter[i]);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+        vertexCount++;
+    }
+
+    // Add the additional vertices for overstroked rrects.
+    // Effectively this is an additional stroked rrect, with its
+    // parameters equal to those in the center of the 9-patch. This will
+    // give constant values across this inner ring.
+    if (kOverstroke_RRectType == args.fType) {
+        float inset = umbraInset + args.fInnerRadius;
+
+        // TL
+        position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // TR
+        position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // BL
+        position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+
+        // BR
+        position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
+        shadowColor[vertexCount] = color;
+        shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+        vertexCount++;
+    }
+}
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry) {
+    if (shadowGeometry.fIsCircle) {
+        return circle_type_to_vert_count(shadowGeometry.fType);
+    }
+
+    return rrect_type_to_vert_count(shadowGeometry.fType);
+}
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry) {
+    if (shadowGeometry.fIsCircle) {
+        return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
+    }
+
+    return rrect_type_to_index_count(shadowGeometry.fType);
+}
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
+                             Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+                             Mesh::VertexArray<vec3> shadowParams) {
+    if (shadowGeometry.fIsCircle) {
+        fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
+                          shadowParams);
+    } else {
+        fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
+    }
+}
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+                            int startingVertexOffset, uint16_t* indices) {
+    if (shadowGeometry.fIsCircle) {
+        const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
+        for (int i = 0; i < indexCount; ++i) {
+            indices[i] = primIndices[i] + startingVertexOffset;
+        }
+    } else {
+        const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
+        for (int i = 0; i < indexCount; ++i) {
+            indices[i] = primIndices[i] + startingVertexOffset;
+        }
+    }
+}
+
+inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
+                          float lightRadius, float& blurRadius, float& scale, vec2& translate) {
+    float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
+    blurRadius = lightRadius * zRatio;
+    scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
+    translate.x = -zRatio * lightX;
+    translate.y = -zRatio * lightY;
+}
+
+static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
+                                                   float devRadius, float blurRadius,
+                                                   float insetWidth) {
+    // An insetWidth > 1/2 rect width or height indicates a simple fill.
+    const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
+
+    FloatRect bounds = devRect;
+    float innerRadius = 0.0f;
+    float outerRadius = devRadius;
+    float umbraInset;
+
+    RRectType type = kFill_RRectType;
+    if (isCircle) {
+        umbraInset = 0;
+    } else {
+        umbraInset = std::max(outerRadius, blurRadius);
+    }
+
+    // If stroke is greater than width or height, this is still a fill,
+    // otherwise we compute stroke params.
+    if (isCircle) {
+        innerRadius = devRadius - insetWidth;
+        type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+    } else {
+        if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
+            // We don't worry about a real inner radius, we just need to know if we
+            // need to create overstroke vertices.
+            innerRadius = std::max(insetWidth - umbraInset, 0.0f);
+            type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+        }
+    }
+    const bool isStroked = (kStroke_RRectType == type);
+    return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
+                                               blurRadius, bounds, type, isCircle, isStroked});
+}
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+                                                   float casterCornerRadius, float casterZ,
+                                                   bool casterIsTranslucent,
+                                                   const vec4& ambientColor) {
+    float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
+    const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
+    const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
+
+    // Outset the shadow rrect to the border of the penumbra
+    float ambientPathOutset = devSpaceInsetWidth;
+    FloatRect outsetRect(casterRect);
+    outsetRect.left -= ambientPathOutset;
+    outsetRect.top -= ambientPathOutset;
+    outsetRect.right += ambientPathOutset;
+    outsetRect.bottom += ambientPathOutset;
+
+    float outsetRad = casterCornerRadius + ambientPathOutset;
+    if (casterIsTranslucent) {
+        // set a large inset to force a fill
+        devSpaceInsetWidth = outsetRect.getWidth();
+    }
+
+    return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
+                             std::abs(devSpaceInsetWidth));
+}
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+                                                float casterCornerRadius, float casterZ,
+                                                bool casterIsTranslucent, const vec4& spotColor,
+                                                const vec3& lightPosition, float lightRadius) {
+    float devSpaceSpotBlur;
+    float spotScale;
+    vec2 spotOffset;
+    GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
+                  devSpaceSpotBlur, spotScale, spotOffset);
+    // handle scale of radius due to CTM
+    const float srcSpaceSpotBlur = devSpaceSpotBlur;
+
+    // Adjust translate for the effect of the scale.
+    spotOffset.x += spotScale;
+    spotOffset.y += spotScale;
+
+    // Compute the transformed shadow rect
+    ui::Transform shadowTransform;
+    shadowTransform.set(spotOffset.x, spotOffset.y);
+    shadowTransform.set(spotScale, 0, 0, spotScale);
+    FloatRect spotShadowRect = shadowTransform.transform(casterRect);
+    float spotShadowRadius = casterCornerRadius * spotScale;
+
+    // Compute the insetWidth
+    float blurOutset = srcSpaceSpotBlur;
+    float insetWidth = blurOutset;
+    if (casterIsTranslucent) {
+        // If transparent, just do a fill
+        insetWidth += spotShadowRect.getWidth();
+    } else {
+        // For shadows, instead of using a stroke we specify an inset from the penumbra
+        // border. We want to extend this inset area so that it meets up with the caster
+        // geometry. The inset geometry will by default already be inset by the blur width.
+        //
+        // We compare the min and max corners inset by the radius between the original
+        // rrect and the shadow rrect. The distance between the two plus the difference
+        // between the scaled radius and the original radius gives the distance from the
+        // transformed shadow shape to the original shape in that corner. The max
+        // of these gives the maximum distance we need to cover.
+        //
+        // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+        // that to get the full insetWidth.
+        float maxOffset;
+        if (casterCornerRadius <= 0.f) {
+            // Manhattan distance works better for rects
+            maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
+                                          std::abs(spotShadowRect.top - casterRect.top)),
+                                 std::max(std::abs(spotShadowRect.right - casterRect.right),
+                                          std::abs(spotShadowRect.bottom - casterRect.bottom)));
+        } else {
+            float dr = spotShadowRadius - casterCornerRadius;
+            vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
+                                        spotShadowRect.top - casterRect.top + dr);
+            vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
+                                         spotShadowRect.bottom - casterRect.bottom - dr);
+            maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
+                                      dot(lowerRightOffset, lowerRightOffset))) +
+                    dr;
+        }
+        insetWidth += std::max(blurOutset, maxOffset);
+    }
+
+    // Outset the shadow rrect to the border of the penumbra
+    spotShadowRadius += blurOutset;
+    spotShadowRect.left -= blurOutset;
+    spotShadowRect.top -= blurOutset;
+    spotShadowRect.right += blurOutset;
+    spotShadowRect.bottom += blurOutset;
+
+    return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
+                             2.0f * devSpaceSpotBlur, std::abs(insetWidth));
+}
+
+void fillShadowTextureData(uint8_t* data, size_t shadowTextureWidth) {
+    for (int i = 0; i < shadowTextureWidth; i++) {
+        const float d = 1 - i / ((shadowTextureWidth * 1.0f) - 1.0f);
+        data[i] = static_cast<uint8_t>((exp(-4.0f * d * d) - 0.018f) * 255);
+    }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
new file mode 100644
index 0000000..912c8bb
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.h
@@ -0,0 +1,96 @@
+/*
+ * 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 <math/vec4.h>
+#include <renderengine/Mesh.h>
+#include <ui/Rect.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ *   coordinate space and will be transformed by the vertex shader.
+ */
+
+enum RRectType {
+    kFill_RRectType,
+    kStroke_RRectType,
+    kOverstroke_RRectType,
+};
+
+struct Geometry {
+    vec4 fColor;
+    float fOuterRadius;
+    float fUmbraInset;
+    float fInnerRadius;
+    float fBlurRadius;
+    FloatRect fDevBounds;
+    RRectType fType;
+    bool fIsCircle;
+    bool fIsStroked;
+};
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+                                                float casterCornerRadius, float casterZ,
+                                                bool casterIsTranslucent, const vec4& spotColor,
+                                                const vec3& lightPosition, float lightRadius);
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+                                                   float casterCornerRadius, float casterZ,
+                                                   bool casterIsTranslucent,
+                                                   const vec4& ambientColor);
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry);
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry);
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
+                             Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+                             Mesh::VertexArray<vec3> shadowParams);
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+                            int startingVertexOffset, uint16_t* indices);
+
+/**
+ * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
+ * darkness at that spot. Values are determined by an exponential falloff
+ * function provided by UX.
+ *
+ * The texture is used for quick lookup in theshadow shader.
+ *
+ * textureData - filled with shadow texture data that needs to be at least of
+ *               size textureWidth
+ *
+ * textureWidth - width of the texture, height is always 1
+ */
+void fillShadowTextureData(uint8_t* textureData, size_t textureWidth);
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index fe9d909..f4fbf35 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -37,6 +37,8 @@
     glBindAttribLocation(programId, position, "position");
     glBindAttribLocation(programId, texCoords, "texCoords");
     glBindAttribLocation(programId, cropCoords, "cropCoords");
+    glBindAttribLocation(programId, shadowColor, "shadowColor");
+    glBindAttribLocation(programId, shadowParams, "shadowParams");
     glLinkProgram(programId);
 
     GLint status;
@@ -65,6 +67,8 @@
         mSamplerLoc = glGetUniformLocation(programId, "sampler");
         mColorLoc = glGetUniformLocation(programId, "color");
         mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
+        mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
+        mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
         mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
         mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
         mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
@@ -138,6 +142,12 @@
     if (mDisplayMaxLuminanceLoc >= 0) {
         glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
     }
+    if (mMaxMasteringLuminanceLoc >= 0) {
+        glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
+    }
+    if (mMaxContentLuminanceLoc >= 0) {
+        glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
+    }
     if (mCornerRadiusLoc >= 0) {
         glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
     }
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index bc9cf08..fc3755e 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -44,7 +44,13 @@
         texCoords = 1,
 
         /* Crop coordinates, in pixels */
-        cropCoords = 2
+        cropCoords = 2,
+
+        /* Shadow color */
+        shadowColor = 3,
+
+        /* Shadow params */
+        shadowParams = 4,
     };
 
     Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
@@ -90,6 +96,10 @@
 
     /* location of display luminance uniform */
     GLint mDisplayMaxLuminanceLoc;
+    /* location of max mastering luminance uniform */
+    GLint mMaxMasteringLuminanceLoc;
+    /* location of max content luminance uniform */
+    GLint mMaxContentLuminanceLoc;
 
     /* location of transform matrix */
     GLint mInputTransformMatrixLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index d242677..3ae35ec 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -77,9 +77,38 @@
     return f;
 }
 
-void ProgramCache::primeCache(EGLContext context, bool useColorManagement) {
+void ProgramCache::primeCache(
+        EGLContext context, bool useColorManagement, bool toneMapperShaderOnly) {
     auto& cache = mCaches[context];
     uint32_t shaderCount = 0;
+
+    if (toneMapperShaderOnly) {
+        Key shaderKey;
+        // base settings used by HDR->SDR tonemap only
+        shaderKey.set(Key::BLEND_MASK | Key::INPUT_TRANSFORM_MATRIX_MASK |
+                      Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::OUTPUT_TF_MASK |
+                      Key::OPACITY_MASK | Key::ALPHA_MASK |
+                      Key::ROUNDED_CORNERS_MASK | Key::TEXTURE_MASK,
+                      Key::BLEND_NORMAL | Key::INPUT_TRANSFORM_MATRIX_ON |
+                      Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::OUTPUT_TF_SRGB |
+                      Key::OPACITY_OPAQUE | Key::ALPHA_EQ_ONE |
+                      Key::ROUNDED_CORNERS_OFF | Key::TEXTURE_EXT);
+        for (int i = 0; i < 4; i++) {
+            // Cache input transfer for HLG & ST2084
+            shaderKey.set(Key::INPUT_TF_MASK, (i & 1) ?
+                    Key::INPUT_TF_HLG : Key::INPUT_TF_ST2084);
+
+            // Cache Y410 input on or off
+            shaderKey.set(Key::Y410_BT2020_MASK, (i & 2) ?
+                    Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+            if (cache.count(shaderKey) == 0) {
+                cache.emplace(shaderKey, generateProgram(shaderKey));
+                shaderCount++;
+            }
+        }
+        return;
+    }
+
     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,
@@ -145,16 +174,15 @@
             .set(Key::OPACITY_MASK,
                  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)
             .set(Key::ROUNDED_CORNERS_MASK,
-                 description.cornerRadius > 0
-                         ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
-
+                 description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
+            .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
     needs.set(Key::Y410_BT2020_MASK,
               description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
 
@@ -310,9 +338,9 @@
                 default:
                     fs << R"__SHADER__(
                         highp vec3 ToneMap(highp vec3 color) {
-                            const float maxMasteringLumi = 1000.0;
-                            const float maxContentLumi = 1000.0;
-                            const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+                            float maxMasteringLumi = maxMasteringLuminance;
+                            float maxContentLumi = maxContentLuminance;
+                            float maxInLumi = min(maxMasteringLumi, maxContentLumi);
                             float maxOutLumi = displayMaxLuminance;
 
                             float nits = color.y;
@@ -522,7 +550,7 @@
 
 String8 ProgramCache::generateVertexShader(const Key& needs) {
     Formatter vs;
-    if (needs.isTexturing()) {
+    if (needs.hasTextureCoords()) {
         vs << "attribute vec4 texCoords;"
            << "varying vec2 outTexCoords;";
     }
@@ -530,16 +558,26 @@
         vs << "attribute lowp vec4 cropCoords;";
         vs << "varying lowp vec2 outCropCoords;";
     }
+    if (needs.drawShadows()) {
+        vs << "attribute lowp vec4 shadowColor;";
+        vs << "varying lowp vec4 outShadowColor;";
+        vs << "attribute lowp vec4 shadowParams;";
+        vs << "varying lowp vec3 outShadowParams;";
+    }
     vs << "attribute vec4 position;"
        << "uniform mat4 projection;"
        << "uniform mat4 texture;"
        << "void main(void) {" << indent << "gl_Position = projection * position;";
-    if (needs.isTexturing()) {
+    if (needs.hasTextureCoords()) {
         vs << "outTexCoords = (texture * texCoords).st;";
     }
     if (needs.hasRoundedCorners()) {
         vs << "outCropCoords = cropCoords.st;";
     }
+    if (needs.drawShadows()) {
+        vs << "outShadowColor = shadowColor;";
+        vs << "outShadowParams = shadowParams.xyz;";
+    }
     vs << dedent << "}";
     return vs.getString();
 }
@@ -554,11 +592,13 @@
     fs << "precision mediump float;";
 
     if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
-        fs << "uniform samplerExternalOES sampler;"
-           << "varying vec2 outTexCoords;";
+        fs << "uniform samplerExternalOES sampler;";
     } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
-        fs << "uniform sampler2D sampler;"
-           << "varying vec2 outTexCoords;";
+        fs << "uniform sampler2D sampler;";
+    }
+
+    if (needs.hasTextureCoords()) {
+        fs << "varying vec2 outTexCoords;";
     }
 
     if (needs.hasRoundedCorners()) {
@@ -585,6 +625,24 @@
             )__SHADER__";
     }
 
+    if (needs.drawShadows()) {
+        fs << R"__SHADER__(
+            varying lowp vec4 outShadowColor;
+            varying lowp vec3 outShadowParams;
+
+            /**
+             * Returns the shadow color.
+             */
+            vec4 getShadowColor()
+            {
+                lowp float d = length(outShadowParams.xy);
+                vec2 uv = vec2(outShadowParams.z * (1.0 - d), 0.5);
+                lowp float factor = texture2D(sampler, uv).a;
+                return outShadowColor * factor;
+            }
+            )__SHADER__";
+    }
+
     if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
         fs << "uniform vec4 color;";
     }
@@ -604,9 +662,10 @@
     }
 
     if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
-        // Currently, display maximum luminance is needed when doing tone mapping.
         if (needs.needsToneMapping()) {
             fs << "uniform float displayMaxLuminance;";
+            fs << "uniform float maxMasteringLuminance;";
+            fs << "uniform float maxContentLuminance;";
         }
 
         if (needs.hasInputTransformMatrix()) {
@@ -647,25 +706,29 @@
     }
 
     fs << "void main(void) {" << indent;
-    if (needs.isTexturing()) {
-        fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
-        if (needs.isY410BT2020()) {
-            fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
-        }
+    if (needs.drawShadows()) {
+        fs << "gl_FragColor = getShadowColor();";
     } else {
-        fs << "gl_FragColor.rgb = color.rgb;";
-        fs << "gl_FragColor.a = 1.0;";
-    }
-    if (needs.isOpaque()) {
-        fs << "gl_FragColor.a = 1.0;";
-    }
-    if (needs.hasAlpha()) {
-        // modulate the current alpha value with alpha set
-        if (needs.isPremultiplied()) {
-            // ... and the color too if we're premultiplied
-            fs << "gl_FragColor *= color.a;";
+        if (needs.isTexturing()) {
+            fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+            if (needs.isY410BT2020()) {
+                fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+            }
         } else {
-            fs << "gl_FragColor.a *= color.a;";
+            fs << "gl_FragColor.rgb = color.rgb;";
+            fs << "gl_FragColor.a = 1.0;";
+        }
+        if (needs.isOpaque()) {
+            fs << "gl_FragColor.a = 1.0;";
+        }
+        if (needs.hasAlpha()) {
+            // modulate the current alpha value with alpha set
+            if (needs.isPremultiplied()) {
+                // ... and the color too if we're premultiplied
+                fs << "gl_FragColor *= color.a;";
+            } else {
+                fs << "gl_FragColor.a *= color.a;";
+            }
         }
     }
 
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 400ad74..901e631 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -112,6 +112,11 @@
             Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
             Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
             Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
+
+            SHADOW_SHIFT = 13,
+            SHADOW_MASK = 1 << SHADOW_SHIFT,
+            SHADOW_OFF = 0 << SHADOW_SHIFT,
+            SHADOW_ON = 1 << SHADOW_SHIFT,
         };
 
         inline Key() : mKey(0) {}
@@ -123,6 +128,7 @@
         }
 
         inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
+        inline bool hasTextureCoords() const { return isTexturing() && !drawShadows(); }
         inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
         inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
         inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
@@ -130,6 +136,7 @@
         inline bool hasRoundedCorners() const {
             return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
         }
+        inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
         inline bool hasInputTransformMatrix() const {
             return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
         }
@@ -179,7 +186,7 @@
     ~ProgramCache() = default;
 
     // Generate shaders to populate the cache
-    void primeCache(const EGLContext context, bool useColorManagement);
+    void primeCache(const EGLContext context, bool useColorManagement, bool toneMapperShaderOnly);
 
     size_t getSize(const EGLContext context) { return mCaches[context].size(); }
 
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
new file mode 100644
index 0000000..b1a84bd
--- /dev/null
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+#include <cstdint>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+BlurFilter::BlurFilter(GLESRenderEngine& engine)
+      : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+    mMixProgram.compile(getVertexShader(), getMixFragShader());
+    mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
+    mMUvLoc = mMixProgram.getAttributeLocation("aUV");
+    mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
+    mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
+    mMMixLoc = mMixProgram.getUniformLocation("uMix");
+}
+
+status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
+    ATRACE_NAME("BlurFilter::setAsDrawTarget");
+    mRadius = radius;
+
+    if (!mTexturesAllocated) {
+        mDisplayWidth = display.physicalDisplay.width();
+        mDisplayHeight = display.physicalDisplay.height();
+        mCompositionFbo.allocateBuffers(mDisplayWidth, mDisplayHeight);
+
+        const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
+        const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
+        mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
+        allocateTextures();
+        mTexturesAllocated = true;
+    }
+
+    if (mBlurredFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+        ALOGE("Invalid blur buffer");
+        return mBlurredFbo.getStatus();
+    }
+    if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+        ALOGE("Invalid composition buffer");
+        return mCompositionFbo.getStatus();
+    }
+
+    mCompositionFbo.bind();
+    glViewport(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight());
+    return NO_ERROR;
+}
+
+void BlurFilter::drawMesh(GLuint uv, GLuint position) {
+    static constexpr auto size = 2.0f;
+    static constexpr auto translation = 1.0f;
+    GLfloat positions[] = {
+        translation-size, -translation-size,
+        translation-size, -translation+size,
+        translation+size, -translation+size
+    };
+    GLfloat texCoords[] = {
+        0.0f, 0.0f-translation,
+        0.0f, size-translation,
+        size, size-translation
+    };
+
+    // set attributes
+    glEnableVertexAttribArray(uv);
+    glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0, texCoords);
+    glEnableVertexAttribArray(position);
+    glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
+                          positions);
+
+    // draw mesh
+    glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
+    mEngine.checkErrors("Drawing blur mesh");
+}
+
+status_t BlurFilter::render(bool multiPass) {
+    ATRACE_NAME("BlurFilter::render");
+
+    // Now let's scale our blur up. It will be interpolated with the larger composited
+    // texture for the first frames, to hide downscaling artifacts.
+    GLfloat mix = fmin(1.0, mRadius / kMaxCrossFadeRadius);
+
+    // When doing multiple passes, we cannot try to read mCompositionFbo, given that we'll
+    // be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
+    // as large as the screen size.
+    if (mix >= 1 || multiPass) {
+        mBlurredFbo.bindAsReadBuffer();
+        glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
+                          mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+        return NO_ERROR;
+    }
+
+    mMixProgram.useProgram();
+    glUniform1f(mMMixLoc, mix);
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
+    glUniform1i(mMTextureLoc, 0);
+    glActiveTexture(GL_TEXTURE1);
+    glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
+    glUniform1i(mMCompositionTextureLoc, 1);
+    mEngine.checkErrors("Setting final pass uniforms");
+
+    drawMesh(mMUvLoc, mMPosLoc);
+
+    glUseProgram(0);
+    glActiveTexture(GL_TEXTURE0);
+    return NO_ERROR;
+}
+
+string BlurFilter::getVertexShader() const {
+    return R"SHADER(
+        #version 310 es
+
+        in vec2 aPosition;
+        in highp vec2 aUV;
+        out highp vec2 vUV;
+
+        void main() {
+            vUV = aUV;
+            gl_Position = vec4(aPosition, 0.0, 1.0);
+        }
+    )SHADER";
+}
+
+string BlurFilter::getMixFragShader() const {
+    string shader = R"SHADER(
+        #version 310 es
+        precision mediump float;
+
+        in highp vec2 vUV;
+        out vec4 fragColor;
+
+        uniform sampler2D uCompositionTexture;
+        uniform sampler2D uTexture;
+        uniform float uMix;
+
+        void main() {
+            vec4 blurred = texture(uTexture, vUV);
+            vec4 composition = texture(uCompositionTexture, vUV);
+            fragColor = mix(composition, blurred, uMix);
+        }
+    )SHADER";
+    return shader;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
new file mode 100644
index 0000000..52dc8aa
--- /dev/null
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -0,0 +1,77 @@
+/*
+ * 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class BlurFilter {
+public:
+    // Downsample FBO to improve performance
+    static constexpr float kFboScale = 0.25f;
+    // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+    // image, up to this radius.
+    static constexpr float kMaxCrossFadeRadius = 30.0f;
+
+    explicit BlurFilter(GLESRenderEngine& engine);
+    virtual ~BlurFilter(){};
+
+    // Set up render targets, redirecting output to offscreen texture.
+    status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
+    // Allocate any textures needed for the filter.
+    virtual void allocateTextures() = 0;
+    // Execute blur passes, rendering to offscreen texture.
+    virtual status_t prepare() = 0;
+    // Render blur to the bound framebuffer (screen).
+    status_t render(bool multiPass);
+
+protected:
+    uint32_t mRadius;
+    void drawMesh(GLuint uv, GLuint position);
+    string getVertexShader() const;
+
+    GLESRenderEngine& mEngine;
+    // Frame buffer holding the composited background.
+    GLFramebuffer mCompositionFbo;
+    // Frame buffer holding the blur result.
+    GLFramebuffer mBlurredFbo;
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+
+private:
+    string getMixFragShader() const;
+    bool mTexturesAllocated = false;
+
+    GenericProgram mMixProgram;
+    GLuint mMPosLoc;
+    GLuint mMUvLoc;
+    GLuint mMMixLoc;
+    GLuint mMTextureLoc;
+    GLuint mMCompositionTextureLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
new file mode 100644
index 0000000..4d7bf44
--- /dev/null
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GaussianBlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+#include <cstdint>
+
+#include <utils/Trace.h>
+
+#define PI 3.14159265359
+#define THETA 0.352
+#define K 1.0 / (2.0 * THETA * THETA)
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GaussianBlurFilter::GaussianBlurFilter(GLESRenderEngine& engine)
+      : BlurFilter(engine),
+        mVerticalPassFbo(engine),
+        mVerticalProgram(engine),
+        mHorizontalProgram(engine) {
+    mVerticalProgram.compile(getVertexShader(), getFragmentShader(false));
+    mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
+    mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
+    mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
+    mVIncrementLoc = mVerticalProgram.getUniformLocation("uIncrement");
+    mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
+    mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
+
+    mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
+    mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
+    mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
+    mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
+    mHIncrementLoc = mHorizontalProgram.getUniformLocation("uIncrement");
+    mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
+    mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
+}
+
+void GaussianBlurFilter::allocateTextures() {
+    mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
+}
+
+status_t GaussianBlurFilter::prepare() {
+    ATRACE_NAME("GaussianBlurFilter::prepare");
+
+    if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+        ALOGE("Invalid vertical FBO");
+        return mVerticalPassFbo.getStatus();
+    }
+    if (!mVerticalProgram.isValid()) {
+        ALOGE("Invalid vertical shader");
+        return GL_INVALID_OPERATION;
+    }
+    if (!mHorizontalProgram.isValid()) {
+        ALOGE("Invalid horizontal shader");
+        return GL_INVALID_OPERATION;
+    }
+
+    mCompositionFbo.bindAsReadBuffer();
+    mBlurredFbo.bindAsDrawBuffer();
+    glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
+                      0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
+                      GL_COLOR_BUFFER_BIT, GL_LINEAR);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
+    // First, we'll apply the vertical pass, that receives the flattened background layers.
+    mVerticalPassFbo.bind();
+    mVerticalProgram.useProgram();
+
+    // Precompute gaussian bell curve, and send it to the shader to avoid
+    // unnecessary computations.
+    auto samples = min(mRadius, kNumSamples);
+    GLfloat gaussianWeights[kNumSamples] = {};
+    for (size_t i = 0; i < samples; i++) {
+        float normalized = float(i) / samples;
+        gaussianWeights[i] = (float)exp(-K * normalized * normalized);
+    }
+
+    // set uniforms
+    auto width = mVerticalPassFbo.getBufferWidth();
+    auto height = mVerticalPassFbo.getBufferHeight();
+    auto radiusF = fmax(1.0f, mRadius * kFboScale);
+    glViewport(0, 0, width, height);
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
+    glUniform1i(mVTextureLoc, 0);
+    glUniform2f(mVIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+    glUniform1i(mVNumSamplesLoc, samples);
+    glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianWeights);
+    mEngine.checkErrors("Setting vertical-diagonal pass uniforms");
+
+    drawMesh(mVUvLoc, mVPosLoc);
+
+    // Blur vertically on a secondary pass
+    mBlurredFbo.bind();
+    mHorizontalProgram.useProgram();
+
+    // set uniforms
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
+    glUniform1i(mHTextureLoc, 0);
+    glUniform2f(mHIncrementLoc, radiusF / (width * 2.0f), radiusF / (height * 2.0f));
+    glUniform1i(mHNumSamplesLoc, samples);
+    glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianWeights);
+    mEngine.checkErrors("Setting vertical pass uniforms");
+
+    drawMesh(mHUvLoc, mHPosLoc);
+
+    // reset active texture
+    mBlurredFbo.unbind();
+    glActiveTexture(GL_TEXTURE0);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    // unbind program
+    glUseProgram(0);
+
+    return NO_ERROR;
+}
+
+string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
+    stringstream shader;
+    shader << "#version 310 es\n"
+           << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
+           << "#define NUM_SAMPLES " << kNumSamples <<
+            R"SHADER(
+        precision mediump float;
+
+        uniform sampler2D uTexture;
+        uniform vec2 uIncrement;
+        uniform float[NUM_SAMPLES] uGaussianWeights;
+        uniform int uSamples;
+
+        highp in vec2 vUV;
+        out vec4 fragColor;
+
+        vec3 gaussianBlur(sampler2D texture, highp vec2 uv, float inc, vec2 direction) {
+            float totalWeight = 0.0;
+            vec3 blurred = vec3(0.0);
+            float fSamples = 1.0 / float(uSamples);
+
+            for (int i = -uSamples; i <= uSamples; i++) {
+                float weight = uGaussianWeights[abs(i)];
+                float normalized = float(i) * fSamples;
+                float radInc = inc * normalized;
+                blurred += weight * (texture(texture, radInc * direction + uv, 0.0)).rgb;
+                totalWeight += weight;
+            }
+
+            return blurred / totalWeight;
+        }
+
+        void main() {
+            #if DIRECTION == 1
+            vec3 color = gaussianBlur(uTexture, vUV, uIncrement.x, vec2(1.0, 0.0));
+            #else
+            vec3 color = gaussianBlur(uTexture, vUV, uIncrement.y, vec2(0.0, 1.0));
+            #endif
+            fragColor = vec4(color, 1.0);
+        }
+
+    )SHADER";
+    return shader.str();
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
new file mode 100644
index 0000000..8580522
--- /dev/null
+++ b/libs/renderengine/gl/filters/GaussianBlurFilter.h
@@ -0,0 +1,66 @@
+/*
+ * 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "BlurFilter.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GaussianBlurFilter : public BlurFilter {
+public:
+    static constexpr uint32_t kNumSamples = 12;
+
+    explicit GaussianBlurFilter(GLESRenderEngine& engine);
+    status_t prepare() override;
+    void allocateTextures() override;
+
+private:
+    string getFragmentShader(bool horizontal) const;
+
+    // Initial, vertical render pass
+    GLFramebuffer mVerticalPassFbo;
+
+    // Vertical pass and its uniforms
+    GenericProgram mVerticalProgram;
+    GLuint mVPosLoc;
+    GLuint mVUvLoc;
+    GLuint mVTextureLoc;
+    GLuint mVIncrementLoc;
+    GLuint mVNumSamplesLoc;
+    GLuint mVGaussianWeightLoc;
+
+    // Horizontal pass and its uniforms
+    GenericProgram mHorizontalProgram;
+    GLuint mHPosLoc;
+    GLuint mHUvLoc;
+    GLuint mHTextureLoc;
+    GLuint mHIncrementLoc;
+    GLuint mHNumSamplesLoc;
+    GLuint mHGaussianWeightLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/gl/filters/GenericProgram.cpp b/libs/renderengine/gl/filters/GenericProgram.cpp
new file mode 100644
index 0000000..bb35889
--- /dev/null
+++ b/libs/renderengine/gl/filters/GenericProgram.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 "GenericProgram.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GenericProgram::GenericProgram(GLESRenderEngine& engine) : mEngine(engine) {}
+
+GenericProgram::~GenericProgram() {
+    if (mVertexShaderHandle != 0) {
+        if (mProgramHandle != 0) {
+            glDetachShader(mProgramHandle, mVertexShaderHandle);
+        }
+        glDeleteShader(mVertexShaderHandle);
+    }
+
+    if (mFragmentShaderHandle != 0) {
+        if (mProgramHandle != 0) {
+            glDetachShader(mProgramHandle, mFragmentShaderHandle);
+        }
+        glDeleteShader(mFragmentShaderHandle);
+    }
+
+    if (mProgramHandle != 0) {
+        glDeleteProgram(mProgramHandle);
+    }
+}
+
+void GenericProgram::compile(string vertexShader, string fragmentShader) {
+    mVertexShaderHandle = compileShader(GL_VERTEX_SHADER, vertexShader);
+    mFragmentShaderHandle = compileShader(GL_FRAGMENT_SHADER, fragmentShader);
+    if (mVertexShaderHandle == 0 || mFragmentShaderHandle == 0) {
+        ALOGE("Aborting program creation.");
+        return;
+    }
+    mProgramHandle = createAndLink(mVertexShaderHandle, mFragmentShaderHandle);
+    mEngine.checkErrors("Linking program");
+}
+
+void GenericProgram::useProgram() const {
+    glUseProgram(mProgramHandle);
+}
+
+GLuint GenericProgram::compileShader(GLuint type, string src) const {
+    const GLuint shader = glCreateShader(type);
+    if (shader == 0) {
+        mEngine.checkErrors("Creating shader");
+        return 0;
+    }
+    const GLchar* charSrc = (const GLchar*)src.c_str();
+    glShaderSource(shader, 1, &charSrc, nullptr);
+    glCompileShader(shader);
+
+    GLint isCompiled = 0;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
+    if (isCompiled == GL_FALSE) {
+        GLint maxLength = 0;
+        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
+        string errorLog;
+        errorLog.reserve(maxLength);
+        glGetShaderInfoLog(shader, maxLength, &maxLength, errorLog.data());
+        glDeleteShader(shader);
+        ALOGE("Error compiling shader: %s", errorLog.c_str());
+        return 0;
+    }
+    return shader;
+}
+GLuint GenericProgram::createAndLink(GLuint vertexShader, GLuint fragmentShader) const {
+    const GLuint program = glCreateProgram();
+    mEngine.checkErrors("Creating program");
+
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, fragmentShader);
+    glLinkProgram(program);
+    mEngine.checkErrors("Linking program");
+    return program;
+}
+
+GLuint GenericProgram::getUniformLocation(const string name) const {
+    if (mProgramHandle == 0) {
+        ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+        return -1;
+    }
+    return glGetUniformLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+GLuint GenericProgram::getAttributeLocation(const string name) const {
+    if (mProgramHandle == 0) {
+        ALOGE("Can't get location of %s on an invalid program.", name.c_str());
+        return -1;
+    }
+    return glGetAttribLocation(mProgramHandle, (const GLchar*)name.c_str());
+}
+
+bool GenericProgram::isValid() const {
+    return mProgramHandle != 0;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/GenericProgram.h b/libs/renderengine/gl/filters/GenericProgram.h
new file mode 100644
index 0000000..6da2a5a
--- /dev/null
+++ b/libs/renderengine/gl/filters/GenericProgram.h
@@ -0,0 +1,51 @@
+/*
+ * 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GenericProgram {
+public:
+    explicit GenericProgram(GLESRenderEngine& renderEngine);
+    ~GenericProgram();
+    void compile(string vertexShader, string fragmentShader);
+    bool isValid() const;
+    void useProgram() const;
+    GLuint getAttributeLocation(const string name) const;
+    GLuint getUniformLocation(const string name) const;
+
+private:
+    GLuint compileShader(GLuint type, const string src) const;
+    GLuint createAndLink(GLuint vertexShader, GLuint fragmentShader) const;
+
+    GLESRenderEngine& mEngine;
+    GLuint mVertexShaderHandle = 0;
+    GLuint mFragmentShaderHandle = 0;
+    GLuint mProgramHandle = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp b/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
new file mode 100644
index 0000000..7524c6d
--- /dev/null
+++ b/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "KawaseBlurFilter.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+#include <ui/GraphicTypes.h>
+
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+KawaseBlurFilter::KawaseBlurFilter(GLESRenderEngine& engine)
+      : BlurFilter(engine), mFbo(engine), mProgram(engine) {
+    mProgram.compile(getVertexShader(), getFragmentShader());
+    mPosLoc = mProgram.getAttributeLocation("aPosition");
+    mUvLoc = mProgram.getAttributeLocation("aUV");
+    mTextureLoc = mProgram.getUniformLocation("uTexture");
+    mOffsetLoc = mProgram.getUniformLocation("uOffset");
+}
+
+void KawaseBlurFilter::allocateTextures() {
+    mFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
+}
+
+status_t KawaseBlurFilter::prepare() {
+    ATRACE_NAME("KawaseBlurFilter::prepare");
+
+    if (mFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+        ALOGE("Invalid FBO");
+        return mFbo.getStatus();
+    }
+    if (!mProgram.isValid()) {
+        ALOGE("Invalid shader");
+        return GL_INVALID_OPERATION;
+    }
+
+    blit(mCompositionFbo, mBlurredFbo);
+
+    // Kawase is an approximation of Gaussian, but it behaves differently from it.
+    // A radius transformation is required for approximating them, and also to introduce
+    // non-integer steps, necessary to smoothly interpolate large radii.
+    auto radius = mRadius / 6.0f;
+
+    // Calculate how many passes we'll do, based on the radius.
+    // Too many passes will make the operation expensive.
+    auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+
+    // We'll ping pong between our textures, to accumulate the result of various offsets.
+    mProgram.useProgram();
+    GLFramebuffer* draw = &mFbo;
+    GLFramebuffer* read = &mBlurredFbo;
+    float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
+    float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
+    glActiveTexture(GL_TEXTURE0);
+    glUniform1i(mTextureLoc, 0);
+    for (auto i = 0; i < passes; i++) {
+        ATRACE_NAME("KawaseBlurFilter::renderPass");
+        draw->bind();
+
+        glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+        glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+        glUniform2f(mOffsetLoc, stepX * i, stepY * i);
+        mEngine.checkErrors("Setting uniforms");
+
+        drawMesh(mUvLoc, mPosLoc);
+
+        // Swap buffers for next iteration
+        auto tmp = draw;
+        draw = read;
+        read = tmp;
+    }
+
+    // Copy texture, given that we're expected to end on mBlurredFbo.
+    if (draw == &mBlurredFbo) {
+        blit(mFbo, mBlurredFbo);
+    }
+
+    // Cleanup
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+    return NO_ERROR;
+}
+
+string KawaseBlurFilter::getFragmentShader() const {
+    return R"SHADER(#version 310 es
+        precision mediump float;
+
+        uniform sampler2D uTexture;
+        uniform vec2 uOffset;
+
+        highp in vec2 vUV;
+        out vec4 fragColor;
+
+        void main() {
+            fragColor  = texture(uTexture, vUV, 0.0);
+            fragColor += texture(uTexture, vUV + vec2( uOffset.x,  uOffset.y), 0.0);
+            fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
+            fragColor += texture(uTexture, vUV + vec2(-uOffset.x,  uOffset.y), 0.0);
+            fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
+
+            fragColor = vec4(fragColor.rgb * 0.2, 1.0);
+        }
+    )SHADER";
+}
+
+void KawaseBlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
+    read.bindAsReadBuffer();
+    draw.bindAsDrawBuffer();
+    glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
+                      draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
+                      GL_LINEAR);
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.h b/libs/renderengine/gl/filters/KawaseBlurFilter.h
new file mode 100644
index 0000000..20009cf
--- /dev/null
+++ b/libs/renderengine/gl/filters/KawaseBlurFilter.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ui/GraphicTypes.h>
+#include "../GLESRenderEngine.h"
+#include "../GLFramebuffer.h"
+#include "BlurFilter.h"
+#include "GenericProgram.h"
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class KawaseBlurFilter : public BlurFilter {
+public:
+    static constexpr uint32_t kMaxPasses = 6;
+
+    explicit KawaseBlurFilter(GLESRenderEngine& engine);
+    status_t prepare() override;
+    void allocateTextures() override;
+
+private:
+    string getFragmentShader() const;
+    void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
+
+    GLFramebuffer mFbo;
+
+    GenericProgram mProgram;
+    GLuint mPosLoc;
+    GLuint mUvLoc;
+    GLuint mTextureLoc;
+    GLuint mOffsetLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 9c9884a..c4a29a9 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <iosfwd>
+
 #include <math/mat4.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
@@ -62,5 +64,31 @@
     uint32_t orientation = ui::Transform::ROT_0;
 };
 
+static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
+    return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
+            lhs.globalTransform == rhs.globalTransform && lhs.maxLuminance == rhs.maxLuminance &&
+            lhs.outputDataspace == rhs.outputDataspace &&
+            lhs.colorTransform == rhs.colorTransform &&
+            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) {
+    *os << "DisplaySettings {";
+    *os << "\n    .physicalDisplay = ";
+    PrintTo(settings.physicalDisplay, os);
+    *os << "\n    .clip = ";
+    PrintTo(settings.clip, os);
+    *os << "\n    .globalTransform = " << settings.globalTransform;
+    *os << "\n    .maxLuminance = " << settings.maxLuminance;
+    *os << "\n    .outputDataspace = ";
+    PrintTo(settings.outputDataspace, os);
+    *os << "\n    .colorTransform = " << settings.colorTransform;
+    *os << "\n    .clearRegion = ";
+    PrintTo(settings.clearRegion, os);
+    *os << "\n    .orientation = " << settings.orientation;
+    *os << "\n}";
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index b8bf801..95e9367 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <iosfwd>
+
 #include <math/mat4.h>
 #include <math/vec3.h>
 #include <renderengine/Texture.h>
@@ -50,7 +52,7 @@
     // Transform matrix to apply to texture coordinates.
     mat4 textureTransform = mat4();
 
-    // Wheteher to use pre-multiplied alpha
+    // Whether to use pre-multiplied alpha.
     bool usePremultipliedAlpha = true;
 
     // Override flag that alpha for each pixel in the buffer *must* be 1.0.
@@ -60,6 +62,8 @@
 
     // HDR color-space setting for Y410.
     bool isY410BT2020 = false;
+    float maxMasteringLuminance = 0.0;
+    float maxContentLuminance = 0.0;
 };
 
 // Metadata describing the layer geometry.
@@ -96,6 +100,33 @@
     half3 solidColor = half3(0.0f, 0.0f, 0.0f);
 };
 
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+    // Color to the ambient shadow. The alpha is premultiplied.
+    vec4 ambientColor = vec4();
+
+    // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+    // depends on the light position.
+    vec4 spotColor = vec4();
+
+    // Position of the light source used to cast the spot shadow.
+    vec3 lightPos = vec3();
+
+    // Radius of the spot light source. Smaller radius will have sharper edges,
+    // larger radius will have softer shadows
+    float lightRadius = 0.f;
+
+    // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+    float length = 0.f;
+
+    // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+    // Otherwise the shadow will only be drawn around the edges of the casting layer.
+    bool casterIsTranslucent = false;
+};
+
 // The settings that RenderEngine requires for correctly rendering a Layer.
 struct LayerSettings {
     // Geometry information
@@ -116,7 +147,112 @@
 
     // True if blending will be forced to be disabled.
     bool disableBlending = false;
+
+    ShadowSettings shadow;
+
+    int backgroundBlurRadius = 0;
 };
 
+// Keep in sync with custom comparison function in
+// compositionengine/impl/ClientCompositionRequestCache.cpp
+static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
+    return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
+            lhs.textureName == rhs.textureName &&
+            lhs.useTextureFiltering == rhs.useTextureFiltering &&
+            lhs.textureTransform == rhs.textureTransform &&
+            lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+            lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+            lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+            lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
+    return lhs.boundaries == rhs.boundaries && lhs.positionTransform == rhs.positionTransform &&
+            lhs.roundedCornersRadius == rhs.roundedCornersRadius &&
+            lhs.roundedCornersCrop == rhs.roundedCornersCrop;
+}
+
+static inline bool operator==(const PixelSource& lhs, const PixelSource& rhs) {
+    return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
+}
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+    return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
+            lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
+            lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+    return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
+            lhs.sourceDataspace == rhs.sourceDataspace &&
+            lhs.colorTransform == rhs.colorTransform &&
+            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+            lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
+}
+
+// Defining PrintTo helps with Google Tests.
+
+static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
+    *os << "Buffer {";
+    *os << "\n    .buffer = " << settings.buffer.get();
+    *os << "\n    .fence = " << settings.fence.get();
+    *os << "\n    .textureName = " << settings.textureName;
+    *os << "\n    .useTextureFiltering = " << settings.useTextureFiltering;
+    *os << "\n    .textureTransform = " << settings.textureTransform;
+    *os << "\n    .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
+    *os << "\n    .isOpaque = " << settings.isOpaque;
+    *os << "\n    .isY410BT2020 = " << settings.isY410BT2020;
+    *os << "\n    .maxMasteringLuminance = " << settings.maxMasteringLuminance;
+    *os << "\n    .maxContentLuminance = " << settings.maxContentLuminance;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const Geometry& settings, ::std::ostream* os) {
+    *os << "Geometry {";
+    *os << "\n    .boundaries = ";
+    PrintTo(settings.boundaries, os);
+    *os << "\n    .positionTransform = " << settings.positionTransform;
+    *os << "\n    .roundedCornersRadius = " << settings.roundedCornersRadius;
+    *os << "\n    .roundedCornersCrop = ";
+    PrintTo(settings.roundedCornersCrop, os);
+    *os << "\n}";
+}
+
+static inline void PrintTo(const PixelSource& settings, ::std::ostream* os) {
+    *os << "PixelSource {";
+    *os << "\n    .buffer = ";
+    PrintTo(settings.buffer, os);
+    *os << "\n    .solidColor = " << settings.solidColor;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
+    *os << "ShadowSettings {";
+    *os << "\n    .ambientColor = " << settings.ambientColor;
+    *os << "\n    .spotColor = " << settings.spotColor;
+    *os << "\n    .lightPos = " << settings.lightPos;
+    *os << "\n    .lightRadius = " << settings.lightRadius;
+    *os << "\n    .length = " << settings.length;
+    *os << "\n    .casterIsTranslucent = " << settings.casterIsTranslucent;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
+    *os << "LayerSettings {";
+    *os << "\n    .geometry = ";
+    PrintTo(settings.geometry, os);
+    *os << "\n    .source = ";
+    PrintTo(settings.source, os);
+    *os << "\n    .alpha = " << settings.alpha;
+    *os << "\n    .sourceDataspace = ";
+    PrintTo(settings.sourceDataspace, os);
+    *os << "\n    .colorTransform = " << settings.colorTransform;
+    *os << "\n    .disableBlending = " << settings.disableBlending;
+    *os << "\n    .backgroundBlurRadius = " << settings.backgroundBlurRadius;
+    *os << "\n    .shadow = ";
+    PrintTo(settings.shadow, os);
+    *os << "\n}";
+}
+
 } // namespace renderengine
 } // namespace android
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
index 7618424..167f13f 100644
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -26,13 +26,14 @@
 
 class Mesh {
 public:
+    class Builder;
+
     enum Primitive {
         TRIANGLES = 0x0004,      // GL_TRIANGLES
         TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
         TRIANGLE_FAN = 0x0006    // GL_TRIANGLE_FAN
     };
 
-    Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0);
     ~Mesh() = default;
 
     /*
@@ -43,12 +44,20 @@
         friend class Mesh;
         float* mData;
         size_t mStride;
+        size_t mOffset = 0;
         VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
 
     public:
-        TYPE& operator[](size_t index) { return *reinterpret_cast<TYPE*>(&mData[index * mStride]); }
+        // Returns a vertex array at an offset so its easier to append attributes from
+        // multiple sources.
+        VertexArray(VertexArray<TYPE>& other, size_t offset)
+              : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
+
+        TYPE& operator[](size_t index) {
+            return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
+        }
         TYPE const& operator[](size_t index) const {
-            return *reinterpret_cast<TYPE const*>(&mData[index * mStride]);
+            return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]);
         }
     };
 
@@ -67,6 +76,18 @@
         return VertexArray<TYPE>(getCropCoords(), mStride);
     }
 
+    template <typename TYPE>
+    VertexArray<TYPE> getShadowColorArray() {
+        return VertexArray<TYPE>(getShadowColor(), mStride);
+    }
+
+    template <typename TYPE>
+    VertexArray<TYPE> getShadowParamsArray() {
+        return VertexArray<TYPE>(getShadowParams(), mStride);
+    }
+
+    uint16_t* getIndicesArray() { return getIndices(); }
+
     Primitive getPrimitive() const;
 
     // returns a pointer to the vertices positions
@@ -78,6 +99,15 @@
     // returns a pointer to the vertices crop coordinates
     float const* getCropCoords() const;
 
+    // returns a pointer to colors
+    float const* getShadowColor() const;
+
+    // returns a pointer to the shadow params
+    float const* getShadowParams() const;
+
+    // returns a pointer to indices
+    uint16_t const* getIndices() const;
+
     // number of vertices in this mesh
     size_t getVertexCount() const;
 
@@ -87,6 +117,12 @@
     // dimension of texture coordinates
     size_t getTexCoordsSize() const;
 
+    size_t getShadowParamsSize() const;
+
+    size_t getShadowColorSize() const;
+
+    size_t getIndexCount() const;
+
     // return stride in bytes
     size_t getByteStride() const;
 
@@ -94,6 +130,8 @@
     size_t getStride() const;
 
 private:
+    Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+         size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount);
     Mesh(const Mesh&);
     Mesh& operator=(const Mesh&);
     Mesh const& operator=(const Mesh&) const;
@@ -101,13 +139,65 @@
     float* getPositions();
     float* getTexCoords();
     float* getCropCoords();
+    float* getShadowColor();
+    float* getShadowParams();
+    uint16_t* getIndices();
 
     std::vector<float> mVertices;
     size_t mVertexCount;
     size_t mVertexSize;
     size_t mTexCoordsSize;
+    size_t mCropCoordsSize;
+    size_t mShadowColorSize;
+    size_t mShadowParamsSize;
     size_t mStride;
     Primitive mPrimitive;
+    std::vector<uint16_t> mIndices;
+    size_t mIndexCount;
+};
+
+class Mesh::Builder {
+public:
+    Builder& setPrimitive(Primitive primitive) {
+        mPrimitive = primitive;
+        return *this;
+    };
+    Builder& setVertices(size_t vertexCount, size_t vertexSize) {
+        mVertexCount = vertexCount;
+        mVertexSize = vertexSize;
+        return *this;
+    };
+    Builder& setTexCoords(size_t texCoordsSize) {
+        mTexCoordsSize = texCoordsSize;
+        return *this;
+    };
+    Builder& setCropCoords(size_t cropCoordsSize) {
+        mCropCoordsSize = cropCoordsSize;
+        return *this;
+    };
+    Builder& setShadowAttrs() {
+        mShadowParamsSize = 3;
+        mShadowColorSize = 4;
+        return *this;
+    };
+    Builder& setIndices(size_t indexCount) {
+        mIndexCount = indexCount;
+        return *this;
+    };
+    Mesh build() const {
+        return Mesh{mPrimitive,      mVertexCount,     mVertexSize,       mTexCoordsSize,
+                    mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount};
+    }
+
+private:
+    size_t mVertexCount = 0;
+    size_t mVertexSize = 0;
+    size_t mTexCoordsSize = 0;
+    size_t mCropCoordsSize = 0;
+    size_t mShadowColorSize = 0;
+    size_t mShadowParamsSize = 0;
+    size_t mIndexCount = 0;
+    Primitive mPrimitive;
 };
 
 } // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index c6a7bd8..46f3fc6 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -48,6 +48,7 @@
 class Image;
 class Mesh;
 class Texture;
+struct RenderEngineCreationArgs;
 
 namespace impl {
 class RenderEngine;
@@ -60,16 +61,13 @@
 
 class RenderEngine {
 public:
-    enum FeatureFlag {
-        USE_COLOR_MANAGEMENT = 1 << 0,      // Device manages color
-        USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context
-
-        // Create a protected context when if possible
-        ENABLE_PROTECTED_CONTEXT = 1 << 2,
+    enum class ContextPriority {
+        LOW = 1,
+        MEDIUM = 2,
+        HIGH = 3,
     };
 
-    static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags,
-                                                      uint32_t imageCacheSize);
+    static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args);
 
     virtual ~RenderEngine() = 0;
 
@@ -77,10 +75,6 @@
     // 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.
@@ -88,24 +82,6 @@
 
     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 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;
@@ -136,40 +112,6 @@
     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;
@@ -212,7 +154,7 @@
     // @return An error code indicating whether drawing was successful. For
     // now, this always returns NO_ERROR.
     virtual status_t drawLayers(const DisplaySettings& display,
-                                const std::vector<LayerSettings>& layers,
+                                const std::vector<const LayerSettings*>& layers,
                                 ANativeWindowBuffer* buffer, const bool useFramebufferCache,
                                 base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0;
 
@@ -226,6 +168,85 @@
     friend class BindNativeBufferAsFramebuffer;
 };
 
+struct RenderEngineCreationArgs {
+    int pixelFormat;
+    uint32_t imageCacheSize;
+    bool useColorManagement;
+    bool enableProtectedContext;
+    bool precacheToneMapperShaderOnly;
+    bool supportsBackgroundBlur;
+    RenderEngine::ContextPriority contextPriority;
+
+    struct Builder;
+
+private:
+    // must be created by Builder via constructor with full argument list
+    RenderEngineCreationArgs(
+            int _pixelFormat,
+            uint32_t _imageCacheSize,
+            bool _useColorManagement,
+            bool _enableProtectedContext,
+            bool _precacheToneMapperShaderOnly,
+            bool _supportsBackgroundBlur,
+            RenderEngine::ContextPriority _contextPriority)
+        : pixelFormat(_pixelFormat)
+        , imageCacheSize(_imageCacheSize)
+        , useColorManagement(_useColorManagement)
+        , enableProtectedContext(_enableProtectedContext)
+        , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly)
+        , supportsBackgroundBlur(_supportsBackgroundBlur)
+        , contextPriority(_contextPriority) {}
+    RenderEngineCreationArgs() = delete;
+};
+
+struct RenderEngineCreationArgs::Builder {
+    Builder() {}
+
+    Builder& setPixelFormat(int pixelFormat) {
+        this->pixelFormat = pixelFormat;
+        return *this;
+    }
+    Builder& setImageCacheSize(uint32_t imageCacheSize) {
+        this->imageCacheSize = imageCacheSize;
+        return *this;
+    }
+    Builder& setUseColorManagerment(bool useColorManagement) {
+        this->useColorManagement = useColorManagement;
+        return *this;
+    }
+    Builder& setEnableProtectedContext(bool enableProtectedContext) {
+        this->enableProtectedContext = enableProtectedContext;
+        return *this;
+    }
+    Builder& setPrecacheToneMapperShaderOnly(bool precacheToneMapperShaderOnly) {
+        this->precacheToneMapperShaderOnly = precacheToneMapperShaderOnly;
+        return *this;
+    }
+    Builder& setSupportsBackgroundBlur(bool supportsBackgroundBlur) {
+        this->supportsBackgroundBlur = supportsBackgroundBlur;
+        return *this;
+    }
+    Builder& setContextPriority(RenderEngine::ContextPriority contextPriority) {
+        this->contextPriority = contextPriority;
+        return *this;
+    }
+    RenderEngineCreationArgs build() const {
+        return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement,
+                                        enableProtectedContext, precacheToneMapperShaderOnly,
+                                        supportsBackgroundBlur, contextPriority);
+    }
+
+private:
+    // 1 means RGBA_8888
+    int pixelFormat = 1;
+    uint32_t imageCacheSize = 0;
+    bool useColorManagement = true;
+    bool enableProtectedContext = false;
+    bool precacheToneMapperShaderOnly = false;
+    bool supportsBackgroundBlur = false;
+    RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
+};
+
 class BindNativeBufferAsFramebuffer {
 public:
     BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer,
@@ -259,8 +280,8 @@
     bool useWaitSync() const override;
 
 protected:
-    RenderEngine(uint32_t featureFlags);
-    const uint32_t mFeatureFlags;
+    RenderEngine(const RenderEngineCreationArgs& args);
+    const RenderEngineCreationArgs mArgs;
 };
 
 } // namespace impl
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index b4d3ef2..3358c69 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -34,20 +34,12 @@
     RenderEngine();
     ~RenderEngine() override;
 
-    MOCK_METHOD0(createFramebuffer, std::unique_ptr<renderengine::Framebuffer>());
-    MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>());
     MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*());
     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_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&));
@@ -55,22 +47,6 @@
     MOCK_METHOD3(bindExternalTextureBuffer,
                  status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&));
     MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t));
-    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&));
@@ -80,7 +56,7 @@
     MOCK_CONST_METHOD0(supportsProtectedContent, bool());
     MOCK_METHOD1(useProtectedContext, bool(bool));
     MOCK_METHOD6(drawLayers,
-                 status_t(const DisplaySettings&, const std::vector<LayerSettings>&,
+                 status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&,
                           ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*));
 };
 
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index bd2055f..a62161a 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -71,6 +71,8 @@
     TransferFunction outputTransferFunction = TransferFunction::LINEAR;
 
     float displayMaxLuminance;
+    float maxMasteringLuminance;
+    float maxContentLuminance;
 
     // projection matrix
     mat4 projectionMatrix;
@@ -79,6 +81,9 @@
     mat4 colorMatrix;
     mat4 inputTransformMatrix;
     mat4 outputTransformMatrix;
+
+    // True if this layer will draw a shadow.
+    bool drawShadows = false;
 };
 
 } // namespace renderengine
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index f47c7fd..afcbc50 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <chrono>
 #include <condition_variable>
+#include <fstream>
 
 #include <gtest/gtest.h>
+#include <cutils/properties.h>
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
 #include <ui/PixelFormat.h>
@@ -26,14 +32,22 @@
 constexpr int DEFAULT_DISPLAY_WIDTH = 128;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
 constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
 
 namespace android {
 
 struct RenderEngineTest : public ::testing::Test {
     static void SetUpTestSuite() {
-        sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>(
-                                                                 ui::PixelFormat::RGBA_8888),
-                                                         0, 1);
+        sRE = renderengine::gl::GLESRenderEngine::create(
+                renderengine::RenderEngineCreationArgs::Builder()
+                    .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+                    .setImageCacheSize(1)
+                    .setUseColorManagerment(false)
+                    .setEnableProtectedContext(false)
+                    .setPrecacheToneMapperShaderOnly(false)
+                    .setSupportsBackgroundBlur(true)
+                    .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
+        .build());
     }
 
     static void TearDownTestSuite() {
@@ -62,21 +76,80 @@
     RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
 
     ~RenderEngineTest() {
+        if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
+            writeBufferToFile("/data/texture_out_");
+        }
         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) {
+    void writeBufferToFile(const char* basename) {
+        std::string filename(basename);
+        filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name());
+        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;
+        }
+
         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;
+        file << "P6\n";
+        file << mBuffer->getWidth() << "\n";
+        file << mBuffer->getHeight() << "\n";
+        file << 255 << "\n";
+
+        std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+        auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
+
+        for (int32_t j = 0; j < mBuffer->getHeight(); j++) {
+            const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
+            for (int32_t i = 0; i < mBuffer->getWidth(); i++) {
+                // Only copy R, G and B components
+                outPtr[0] = src[0];
+                outPtr[1] = src[1];
+                outPtr[2] = src[2];
+                outPtr += 3;
+
+                src += 4;
+            }
+        }
+        file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+        mBuffer->unlock();
+    }
+
+    void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+        size_t c;
+        Rect const* rect = region.getArray(&c);
+        for (size_t i = 0; i < c; i++, rect++) {
+            expectBufferColor(*rect, r, g, b, a);
+        }
+    }
+
+    void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+                           uint8_t tolerance = 0) {
+        auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
+            auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
+                uint8_t tmp = a >= b ? a - b : b - a;
+                return tmp <= tolerance;
+            };
+            return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
         };
+
+        expectBufferColor(rect, r, g, b, a, colorCompare);
+    }
+
+    void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+                           std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
+        uint8_t* pixels;
+        mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                      reinterpret_cast<void**>(&pixels));
         int32_t maxFails = 10;
         int32_t fails = 0;
         for (int32_t j = 0; j < region.getHeight(); j++) {
@@ -84,7 +157,7 @@
                     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);
+                bool equal = colorCompare(src, expected);
                 EXPECT_TRUE(equal)
                         << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
                         << "expected (" << static_cast<uint32_t>(r) << ", "
@@ -105,6 +178,64 @@
         mBuffer->unlock();
     }
 
+    void expectAlpha(const Rect& rect, uint8_t a) {
+        auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
+            return colorA[3] == colorB[3];
+        };
+        expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+    }
+
+    void expectShadowColor(const renderengine::LayerSettings& castingLayer,
+                           const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+                           const ubyte4& backgroundColor) {
+        const Rect casterRect(castingLayer.geometry.boundaries);
+        Region casterRegion = Region(casterRect);
+        const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+        if (casterCornerRadius > 0.0f) {
+            // ignore the corners if a corner radius is set
+            Rect cornerRect(casterCornerRadius, casterCornerRadius);
+            casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.left, casterRect.top));
+            casterRegion.subtractSelf(
+                    cornerRect.offsetTo(casterRect.right - casterCornerRadius, casterRect.top));
+            casterRegion.subtractSelf(
+                    cornerRect.offsetTo(casterRect.left, casterRect.bottom - casterCornerRadius));
+            casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.right - casterCornerRadius,
+                                                          casterRect.bottom - casterCornerRadius));
+        }
+
+        const float shadowInset = shadow.length * -1.0f;
+        const Rect casterWithShadow =
+                Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+        const Region shadowRegion = Region(casterWithShadow).subtractSelf(casterRect);
+        const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+
+        // verify casting layer
+        expectBufferColor(casterRegion, casterColor.r, casterColor.g, casterColor.b, casterColor.a);
+
+        // verify shadows by testing just the alpha since its difficult to validate the shadow color
+        size_t c;
+        Rect const* r = shadowRegion.getArray(&c);
+        for (size_t i = 0; i < c; i++, r++) {
+            expectAlpha(*r, 255);
+        }
+
+        // verify background
+        expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+                          backgroundColor.a);
+    }
+
+    static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+                                                          bool casterIsTranslucent) {
+        renderengine::ShadowSettings shadow;
+        shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
+        shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
+        shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
+        shadow.lightRadius = 0.0f;
+        shadow.length = shadowLength;
+        shadow.casterIsTranslucent = casterIsTranslucent;
+        return shadow;
+    }
+
     static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
 
     static Rect offsetRect() {
@@ -118,7 +249,8 @@
     }
 
     void invokeDraw(renderengine::DisplaySettings settings,
-                    std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
+                    std::vector<const renderengine::LayerSettings*> layers,
+                    sp<GraphicBuffer> buffer) {
         base::unique_fd fence;
         status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true,
                                           base::unique_fd(), &fence);
@@ -138,7 +270,7 @@
 
     void drawEmptyLayers() {
         renderengine::DisplaySettings settings;
-        std::vector<renderengine::LayerSettings> layers;
+        std::vector<const renderengine::LayerSettings*> layers;
         // Meaningless buffer since we don't do any drawing
         sp<GraphicBuffer> buffer = new GraphicBuffer();
         invokeDraw(settings, layers, buffer);
@@ -199,6 +331,9 @@
     void fillBufferWithRoundedCorners();
 
     template <typename SourceVariant>
+    void fillBufferAndBlurBackground();
+
+    template <typename SourceVariant>
     void overlayCorners();
 
     void fillRedBufferTextureTransform();
@@ -219,6 +354,11 @@
 
     void clearRegion();
 
+    template <typename SourceVariant>
+    void drawShadow(const renderengine::LayerSettings& castingLayer,
+                    const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+                    const ubyte4& backgroundColor);
+
     // Keep around the same renderengine object to save on initialization time.
     // For now, exercise the GL backend directly so that some caching specifics
     // can be tested without changing the interface.
@@ -301,14 +441,14 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const 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);
+    layers.push_back(&layer);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -343,14 +483,14 @@
     settings.physicalDisplay = offsetRect();
     settings.clip = offsetRectAtZero();
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const 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);
+    layers.push_back(&layer);
     invokeDraw(settings, layers, mBuffer);
 }
 
@@ -376,7 +516,7 @@
     settings.clip = Rect(2, 2);
     settings.globalTransform = transform;
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layerOne;
     Rect rectOne(0, 0, 1, 1);
@@ -396,9 +536,9 @@
     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);
+    layers.push_back(&layerOne);
+    layers.push_back(&layerTwo);
+    layers.push_back(&layerThree);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -477,7 +617,7 @@
     // Here logical space is 2x2
     settings.clip = Rect(2, 2);
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -487,7 +627,7 @@
     layer.source.solidColor = half3(1.0f, 0.0f, 0.0f);
     layer.alpha = 1.0f;
 
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -508,7 +648,7 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = Rect(1, 1);
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -524,7 +664,7 @@
     layer.alpha = 1.0f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -541,7 +681,7 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
@@ -550,7 +690,7 @@
     SourceVariant::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0f;
 
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -572,12 +712,57 @@
 }
 
 template <typename SourceVariant>
+void RenderEngineTest::fillBufferAndBlurBackground() {
+        char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    auto blurRadius = 50;
+    auto center = DEFAULT_DISPLAY_WIDTH / 2;
+
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    renderengine::LayerSettings backgroundLayer;
+    backgroundLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    SourceVariant::fillColor(backgroundLayer, 0.0f, 1.0f, 0.0f, this);
+    backgroundLayer.alpha = 1.0f;
+    layers.push_back(&backgroundLayer);
+
+    renderengine::LayerSettings leftLayer;
+    leftLayer.geometry.boundaries =
+            Rect(DEFAULT_DISPLAY_WIDTH / 2, DEFAULT_DISPLAY_HEIGHT).toFloatRect();
+    SourceVariant::fillColor(leftLayer, 1.0f, 0.0f, 0.0f, this);
+    leftLayer.alpha = 1.0f;
+    layers.push_back(&leftLayer);
+
+    renderengine::LayerSettings blurLayer;
+    blurLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    blurLayer.backgroundBlurRadius = blurRadius;
+    blurLayer.alpha = 0;
+    layers.push_back(&blurLayer);
+
+    invokeDraw(settings, layers, mBuffer);
+
+    expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255,
+                      50 /* tolerance */);
+    expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255,
+                      50 /* tolerance */);
+}
+
+template <typename SourceVariant>
 void RenderEngineTest::overlayCorners() {
     renderengine::DisplaySettings settings;
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<renderengine::LayerSettings> layersFirst;
+    std::vector<const renderengine::LayerSettings*> layersFirst;
 
     renderengine::LayerSettings layerOne;
     layerOne.geometry.boundaries =
@@ -585,14 +770,14 @@
     SourceVariant::fillColor(layerOne, 1.0f, 0.0f, 0.0f, this);
     layerOne.alpha = 0.2;
 
-    layersFirst.push_back(layerOne);
+    layersFirst.push_back(&layerOne);
     invokeDraw(settings, layersFirst, mBuffer);
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 51, 0, 0, 51);
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3 + 1, DEFAULT_DISPLAY_HEIGHT / 3 + 1,
                            DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                       0, 0, 0, 0);
 
-    std::vector<renderengine::LayerSettings> layersSecond;
+    std::vector<const renderengine::LayerSettings*> layersSecond;
     renderengine::LayerSettings layerTwo;
     layerTwo.geometry.boundaries =
             FloatRect(DEFAULT_DISPLAY_WIDTH / 3.0, DEFAULT_DISPLAY_HEIGHT / 3.0,
@@ -600,7 +785,7 @@
     SourceVariant::fillColor(layerTwo, 0.0f, 1.0f, 0.0f, this);
     layerTwo.alpha = 1.0f;
 
-    layersSecond.push_back(layerTwo);
+    layersSecond.push_back(&layerTwo);
     invokeDraw(settings, layersSecond, mBuffer);
 
     expectBufferColor(Rect(DEFAULT_DISPLAY_WIDTH / 3, DEFAULT_DISPLAY_HEIGHT / 3), 0, 0, 0, 0);
@@ -614,7 +799,7 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = Rect(1, 1);
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
     // Here will allocate a checker board texture, but transform texture
@@ -649,7 +834,7 @@
     layer.alpha = 1.0f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -665,7 +850,7 @@
     // Here logical space is 1x1
     settings.clip = Rect(1, 1);
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
     sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
@@ -688,7 +873,7 @@
     layer.alpha = 0.5f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -704,7 +889,7 @@
     // Here logical space is 1x1
     settings.clip = Rect(1, 1);
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
     sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
@@ -727,7 +912,7 @@
     layer.alpha = 0.5f;
     layer.geometry.boundaries = Rect(1, 1).toFloatRect();
 
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     invokeDraw(settings, layers, mBuffer);
 }
@@ -744,10 +929,10 @@
     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;
+    std::vector<const renderengine::LayerSettings*> layers;
     // dummy layer, without bounds should not render anything
     renderengine::LayerSettings layer;
-    layers.push_back(layer);
+    layers.push_back(&layer);
     invokeDraw(settings, layers, mBuffer);
 }
 
@@ -760,17 +945,51 @@
                       0, 0, 0, 0);
 }
 
+template <typename SourceVariant>
+void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
+                                  const renderengine::ShadowSettings& shadow,
+                                  const ubyte4& casterColor, const ubyte4& backgroundColor) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<const renderengine::LayerSettings*> layers;
+
+    // add background layer
+    renderengine::LayerSettings bgLayer;
+    bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+    ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+                                  backgroundColor.b / 255.0f, this);
+    bgLayer.alpha = backgroundColor.a / 255.0f;
+    layers.push_back(&bgLayer);
+
+    // add shadow layer
+    renderengine::LayerSettings shadowLayer;
+    shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
+    shadowLayer.alpha = castingLayer.alpha;
+    shadowLayer.shadow = shadow;
+    layers.push_back(&shadowLayer);
+
+    // add layer casting the shadow
+    renderengine::LayerSettings layer = castingLayer;
+    SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
+                             casterColor.b / 255.0f, this);
+    layers.push_back(&layer);
+
+    invokeDraw(settings, layers, mBuffer);
+}
+
 TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
     drawEmptyLayers();
 }
 
 TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
     renderengine::DisplaySettings settings;
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
-    layers.push_back(layer);
+    layers.push_back(&layer);
     base::unique_fd fence;
     status_t status = sRE->drawLayers(settings, layers, nullptr, true, base::unique_fd(), &fence);
 
@@ -782,12 +1001,12 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0;
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true,
                                       base::unique_fd(), nullptr);
@@ -801,12 +1020,12 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
     layer.alpha = 1.0;
-    layers.push_back(layer);
+    layers.push_back(&layer);
 
     status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false,
                                       base::unique_fd(), nullptr);
@@ -864,6 +1083,10 @@
     fillBufferWithRoundedCorners<ColorSourceVariant>();
 }
 
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_colorSource) {
+    fillBufferAndBlurBackground<ColorSourceVariant>();
+}
+
 TEST_F(RenderEngineTest, drawLayers_overlayCorners_colorSource) {
     overlayCorners<ColorSourceVariant>();
 }
@@ -916,6 +1139,10 @@
     fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
 
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_opaqueBufferSource) {
+    fillBufferAndBlurBackground<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
 TEST_F(RenderEngineTest, drawLayers_overlayCorners_opaqueBufferSource) {
     overlayCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
 }
@@ -968,6 +1195,10 @@
     fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
 
+TEST_F(RenderEngineTest, drawLayers_fillBufferAndBlurBackground_bufferSource) {
+    fillBufferAndBlurBackground<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
 TEST_F(RenderEngineTest, drawLayers_overlayCorners_bufferSource) {
     overlayCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
 }
@@ -993,13 +1224,13 @@
     settings.physicalDisplay = fullscreenRect();
     settings.clip = fullscreenRect();
 
-    std::vector<renderengine::LayerSettings> layers;
+    std::vector<const renderengine::LayerSettings*> layers;
 
     renderengine::LayerSettings layer;
     layer.geometry.boundaries = fullscreenRect().toFloatRect();
     BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
 
-    layers.push_back(layer);
+    layers.push_back(&layer);
     invokeDraw(settings, layers, mBuffer);
     uint64_t bufferId = layer.source.buffer.buffer->getId();
     EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
@@ -1077,4 +1308,104 @@
     EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
 }
 
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(1, 1);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+                                                              backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.geometry.roundedCornersRadius = 3.0f;
+    castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
+    castingLayer.alpha = 1.0f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              false /* casterIsTranslucent */);
+
+    drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+                                                              backgroundColor);
+    expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+    const ubyte4 casterColor(255, 0, 0, 255);
+    const ubyte4 backgroundColor(255, 255, 255, 255);
+    const float shadowLength = 5.0f;
+    Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+    casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+    renderengine::LayerSettings castingLayer;
+    castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+    castingLayer.alpha = 0.5f;
+    renderengine::ShadowSettings settings =
+            getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+                              true /* casterIsTranslucent */);
+
+    drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+                                                              backgroundColor);
+
+    // verify only the background since the shadow will draw behind the caster
+    const float shadowInset = settings.length * -1.0f;
+    const Rect casterWithShadow =
+            Rect(casterBounds).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+    const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+    expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+                      backgroundColor.a);
+}
+
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 5200545..8ed09f8 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -199,6 +199,10 @@
             int32_t type = data.readInt32();
             int32_t format = data.readInt32();
             native_handle_t *resource = data.readNativeHandle();
+            // Avoid a crash in native_handle_close if resource is nullptr
+            if (resource == nullptr) {
+                return BAD_VALUE;
+            }
             sp<ISensorEventConnection> ch =
                     createSensorDirectConnection(opPackageName, size, type, format, resource);
             native_handle_close(resource);
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index abc9103..9d817ae 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -268,6 +268,10 @@
         mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_UNCALIBRATED;
         mFlags |= SENSOR_FLAG_CONTINUOUS_MODE;
         break;
+    case SENSOR_TYPE_HINGE_ANGLE:
+        mStringType = SENSOR_STRING_TYPE_HINGE_ANGLE;
+        mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
+        break;
     default:
         // Only pipe the stringType, requiredPermission and flags for custom sensors.
         if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) {
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index bf8b9f7..a4a5d13 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -209,7 +209,7 @@
             type == SENSOR_TYPE_TILT_DETECTOR || type == SENSOR_TYPE_WAKE_GESTURE ||
             type == SENSOR_TYPE_GLANCE_GESTURE || type == SENSOR_TYPE_PICK_UP_GESTURE ||
             type == SENSOR_TYPE_WRIST_TILT_GESTURE ||
-            type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT) {
+            type == SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT || type == SENSOR_TYPE_HINGE_ANGLE) {
             wakeUpSensor = true;
         }
         // For now we just return the first sensor of that type we find.
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 080336b..ba6255d 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -46,6 +46,7 @@
         "Gralloc.cpp",
         "Gralloc2.cpp",
         "Gralloc3.cpp",
+        "Gralloc4.cpp",
         "GraphicBuffer.cpp",
         "GraphicBufferAllocator.cpp",
         "GraphicBufferMapper.cpp",
@@ -62,6 +63,10 @@
     include_dirs: [
         "frameworks/native/include",
     ],
+    export_include_dirs: [
+        "include",
+        "include_private",
+    ],
 
     // Uncomment the following line to enable VALIDATE_REGIONS traces
     //defaults: ["libui-validate-regions-defaults"],
@@ -70,12 +75,16 @@
         "android.frameworks.bufferhub@1.0",
         "android.hardware.graphics.allocator@2.0",
         "android.hardware.graphics.allocator@3.0",
+        "android.hardware.graphics.allocator@4.0",
+        "android.hardware.graphics.common-ndk_platform",
         "android.hardware.graphics.common@1.2",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@2.1",
         "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
         "libbase",
         "libcutils",
+        "libgralloctypes",
         "libhidlbase",
         "libsync",
         "libutils",
@@ -84,6 +93,9 @@
 
     export_shared_lib_headers: [
         "android.hardware.graphics.common@1.2",
+        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.mapper@4.0",
+        "libgralloctypes",
     ],
 
     static_libs: [
@@ -166,3 +178,11 @@
     "tests",
     "tools",
 ]
+
+filegroup {
+    name: "libui_host_common",
+    srcs: [
+        "Rect.cpp",
+        "PixelFormat.cpp"
+    ],
+}
diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp
index da91a97..1dfc1e9 100644
--- a/libs/ui/BufferHubBuffer.cpp
+++ b/libs/ui/BufferHubBuffer.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "BufferHubBuffer"
 #include <poll.h>
 
 #include <android-base/unique_fd.h>
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 4ce891e..33ab7c4 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -62,8 +62,26 @@
     int warningTimeout = 3000;
     int err = sync_wait(mFenceFd, warningTimeout);
     if (err < 0 && errno == ETIME) {
-        ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
-                warningTimeout);
+        ALOGE("waitForever: %s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
+              warningTimeout);
+
+        struct sync_file_info* finfo = sync_file_info(mFenceFd);
+        if (finfo) {
+            // status: active(0) signaled(1) error(<0)
+            ALOGI("waitForever: fence(%s) status(%d)", finfo->name, finfo->status);
+
+            struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
+            for (uint32_t i = 0; i < finfo->num_fences; i++) {
+                uint64_t ts_sec = pinfo[i].timestamp_ns / 1000000000LL;
+                uint64_t ts_usec = (pinfo[i].timestamp_ns % 1000000000LL) / 1000LL;
+
+                ALOGI("waitForever: sync point: timeline(%s) drv(%s) status(%d) timestamp(%" PRIu64
+                      ".%06" PRIu64 ")",
+                      pinfo[i].obj_name, pinfo[i].driver_name, pinfo[i].status, ts_sec, ts_usec);
+            }
+            sync_file_info_free(finfo);
+        }
+
         err = sync_wait(mFenceFd, TIMEOUT_NEVER);
     }
     return err < 0 ? -errno : status_t(NO_ERROR);
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 5dc4530..040a62b 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -351,12 +351,6 @@
     return releaseFence;
 }
 
-status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/,
-                                     android::PixelFormat /*format*/, uint32_t /*layerCount*/,
-                                     uint64_t /*usage*/, bool* /*outSupported*/) const {
-    return INVALID_OPERATION;
-}
-
 Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
     mAllocator = IAllocator::getService();
     if (mAllocator == nullptr) {
@@ -369,7 +363,7 @@
     return mAllocator != nullptr;
 }
 
-std::string Gralloc2Allocator::dumpDebugInfo() const {
+std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const {
     std::string debugInfo;
 
     mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -379,9 +373,10 @@
     return debugInfo;
 }
 
-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 {
+status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+                                     PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                     uint32_t bufferCount, uint32_t* outStride,
+                                     buffer_handle_t* outBufferHandles, bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo = {};
     descriptorInfo.width = width;
     descriptorInfo.height = height;
@@ -404,19 +399,33 @@
                                             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;
+                                        if (importBuffers) {
+                                            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;
                                                 }
-                                                return;
+                                            }
+                                        } else {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                outBufferHandles[i] = native_handle_clone(
+                                                        tmpBuffers[i].getNativeHandle());
+                                                if (!outBufferHandles[i]) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        auto buffer = const_cast<native_handle_t*>(
+                                                                outBufferHandles[j]);
+                                                        native_handle_close(buffer);
+                                                        native_handle_delete(buffer);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                }
                                             }
                                         }
-
                                         *outStride = tmpStride;
                                     });
 
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index eb43765..882674f 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -352,7 +352,7 @@
     return mAllocator != nullptr;
 }
 
-std::string Gralloc3Allocator::dumpDebugInfo() const {
+std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const {
     std::string debugInfo;
 
     mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
@@ -360,9 +360,10 @@
     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 {
+status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, 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, bool importBuffers) const {
     IMapper::BufferDescriptorInfo descriptorInfo;
     sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
 
@@ -381,16 +382,31 @@
                                             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;
+                                        if (importBuffers) {
+                                            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;
                                                 }
-                                                return;
+                                            }
+                                        } else {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                outBufferHandles[i] = native_handle_clone(
+                                                        tmpBuffers[i].getNativeHandle());
+                                                if (!outBufferHandles[i]) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        auto buffer = const_cast<native_handle_t*>(
+                                                                outBufferHandles[j]);
+                                                        native_handle_close(buffer);
+                                                        native_handle_delete(buffer);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                }
                                             }
                                         }
                                         *outStride = tmpStride;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
new file mode 100644
index 0000000..30c48c8
--- /dev/null
+++ b/libs/ui/Gralloc4.cpp
@@ -0,0 +1,1125 @@
+/*
+ * 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 "Gralloc4"
+
+#include <hidl/ServiceManagement.h>
+#include <hwbinder/IPCThreadState.h>
+#include <ui/Gralloc4.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 aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using android::hardware::hidl_vec;
+using android::hardware::graphics::allocator::V4_0::IAllocator;
+using android::hardware::graphics::common::V1_2::BufferUsage;
+using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
+using android::hardware::graphics::mapper::V4_0::Error;
+using android::hardware::graphics::mapper::V4_0::IMapper;
+using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
+using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataTypeDescription =
+        android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription;
+
+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_2::BufferUsage>()) {
+            bits = bits | bit;
+        }
+        return bits;
+    }();
+    return validUsageBits;
+}
+
+static inline IMapper::Rect sGralloc4Rect(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(std::string name, uint32_t width, uint32_t height,
+                                         PixelFormat format, uint32_t layerCount, uint64_t usage,
+                                         IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+    outDescriptorInfo->name = name;
+    outDescriptorInfo->width = width;
+    outDescriptorInfo->height = height;
+    outDescriptorInfo->layerCount = layerCount;
+    outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
+    outDescriptorInfo->usage = usage;
+}
+
+} // anonymous namespace
+
+void Gralloc4Mapper::preload() {
+    android::hardware::preloadPassthroughService<IMapper>();
+}
+
+Gralloc4Mapper::Gralloc4Mapper() {
+    mMapper = IMapper::getService();
+    if (mMapper == nullptr) {
+        ALOGI("mapper 4.x is not supported");
+        return;
+    }
+    if (mMapper->isRemote()) {
+        LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode");
+    }
+}
+
+bool Gralloc4Mapper::isLoaded() const {
+    return mMapper != nullptr;
+}
+
+status_t Gralloc4Mapper::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 Gralloc4Mapper::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 Gralloc4Mapper::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 Gralloc4Mapper::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 Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
+                                            uint32_t height, PixelFormat format,
+                                            uint32_t layerCount, uint64_t usage,
+                                            uint32_t stride) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo("validateBufferSize", 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 Gralloc4Mapper::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 Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, void** outData, int32_t* outBytesPerPixel,
+                              int32_t* outBytesPerStride) const {
+    std::vector<ui::PlaneLayout> planeLayouts;
+    status_t err = getPlaneLayouts(bufferHandle, &planeLayouts);
+
+    if (err != NO_ERROR && !planeLayouts.empty()) {
+        if (outBytesPerPixel) {
+            int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits;
+            for (const auto& planeLayout : planeLayouts) {
+                if (bitsPerPixel != planeLayout.sampleIncrementInBits) {
+                    bitsPerPixel = -1;
+                }
+            }
+            if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) {
+                *outBytesPerPixel = bitsPerPixel / 8;
+            } else {
+                *outBytesPerPixel = -1;
+            }
+        }
+        if (outBytesPerStride) {
+            int32_t bytesPerStride = planeLayouts.front().strideInBytes;
+            for (const auto& planeLayout : planeLayouts) {
+                if (bytesPerStride != planeLayout.strideInBytes) {
+                    bytesPerStride = -1;
+                }
+            }
+            if (bytesPerStride >= 0) {
+                *outBytesPerStride = bytesPerStride;
+            } else {
+                *outBytesPerStride = -1;
+            }
+        }
+    }
+
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    IMapper::Rect accessRegion = sGralloc4Rect(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) {
+                                 error = tmpError;
+                                 if (error != Error::NONE) {
+                                     return;
+                                 }
+                                 *outData = tmpData;
+                             });
+
+    // 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 Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+                              int acquireFence, android_ycbcr* outYcbcr) const {
+    if (!outYcbcr) {
+        return BAD_VALUE;
+    }
+
+    std::vector<ui::PlaneLayout> planeLayouts;
+    status_t error = getPlaneLayouts(bufferHandle, &planeLayouts);
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    void* data = nullptr;
+    error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr);
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    android_ycbcr ycbcr;
+
+    ycbcr.y = nullptr;
+    ycbcr.cb = nullptr;
+    ycbcr.cr = nullptr;
+    ycbcr.ystride = 0;
+    ycbcr.cstride = 0;
+    ycbcr.chroma_step = 0;
+
+    for (const auto& planeLayout : planeLayouts) {
+        for (const auto& planeLayoutComponent : planeLayout.components) {
+            if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
+                continue;
+            }
+            if (0 != planeLayoutComponent.offsetInBits % 8) {
+                unlock(bufferHandle);
+                return BAD_VALUE;
+            }
+
+            uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes +
+                    (planeLayoutComponent.offsetInBits / 8);
+            uint64_t sampleIncrementInBytes;
+
+            auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
+            switch (type) {
+                case PlaneLayoutComponentType::Y:
+                    if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) ||
+                        (planeLayout.sampleIncrementInBits != 8)) {
+                        unlock(bufferHandle);
+                        return BAD_VALUE;
+                    }
+                    ycbcr.y = tmpData;
+                    ycbcr.ystride = planeLayout.strideInBytes;
+                    break;
+
+                case PlaneLayoutComponentType::CB:
+                case PlaneLayoutComponentType::CR:
+                    if (planeLayout.sampleIncrementInBits % 8 != 0) {
+                        unlock(bufferHandle);
+                        return BAD_VALUE;
+                    }
+
+                    sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
+                    if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) {
+                        unlock(bufferHandle);
+                        return BAD_VALUE;
+                    }
+
+                    if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) {
+                        ycbcr.cstride = planeLayout.strideInBytes;
+                        ycbcr.chroma_step = sampleIncrementInBytes;
+                    } else {
+                        if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) ||
+                            (ycbcr.chroma_step != sampleIncrementInBytes)) {
+                            unlock(bufferHandle);
+                            return BAD_VALUE;
+                        }
+                    }
+
+                    if (type == PlaneLayoutComponentType::CB) {
+                        if (ycbcr.cb != nullptr) {
+                            unlock(bufferHandle);
+                            return BAD_VALUE;
+                        }
+                        ycbcr.cb = tmpData;
+                    } else {
+                        if (ycbcr.cr != nullptr) {
+                            unlock(bufferHandle);
+                            return BAD_VALUE;
+                        }
+                        ycbcr.cr = tmpData;
+                    }
+                    break;
+                default:
+                    break;
+            };
+        }
+    }
+
+    *outYcbcr = ycbcr;
+    return static_cast<status_t>(Error::UNSUPPORTED);
+}
+
+int Gralloc4Mapper::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;
+}
+
+status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
+                                     uint32_t layerCount, uint64_t usage,
+                                     bool* outSupported) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, &descriptorInfo);
+
+    Error error;
+    auto ret = mMapper->isSupported(descriptorInfo,
+                                    [&](const auto& tmpError, const auto& tmpSupported) {
+                                        error = tmpError;
+                                        if (error != Error::NONE) {
+                                            return;
+                                        }
+                                        if (outSupported) {
+                                            *outSupported = tmpSupported;
+                                        }
+                                    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount,
+              error);
+    }
+
+    return static_cast<status_t>(error);
+}
+
+template <class T>
+status_t Gralloc4Mapper::get(buffer_handle_t bufferHandle, const MetadataType& metadataType,
+                             DecodeFunction<T> decodeFunction, T* outMetadata) const {
+    if (!outMetadata) {
+        return BAD_VALUE;
+    }
+
+    hidl_vec<uint8_t> vec;
+    Error error;
+    auto ret = mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType,
+                            [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
+                                error = tmpError;
+                                vec = tmpVec;
+                            });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("get(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+              metadataType.value, error);
+        return static_cast<status_t>(error);
+    }
+
+    return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const {
+    return get(bufferHandle, gralloc4::MetadataType_BufferId, gralloc4::decodeBufferId,
+               outBufferId);
+}
+
+status_t Gralloc4Mapper::getName(buffer_handle_t bufferHandle, std::string* outName) const {
+    return get(bufferHandle, gralloc4::MetadataType_Name, gralloc4::decodeName, outName);
+}
+
+status_t Gralloc4Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const {
+    return get(bufferHandle, gralloc4::MetadataType_Width, gralloc4::decodeWidth, outWidth);
+}
+
+status_t Gralloc4Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const {
+    return get(bufferHandle, gralloc4::MetadataType_Height, gralloc4::decodeHeight, outHeight);
+}
+
+status_t Gralloc4Mapper::getLayerCount(buffer_handle_t bufferHandle,
+                                       uint64_t* outLayerCount) const {
+    return get(bufferHandle, gralloc4::MetadataType_LayerCount, gralloc4::decodeLayerCount,
+               outLayerCount);
+}
+
+status_t Gralloc4Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                                 ui::PixelFormat* outPixelFormatRequested) const {
+    return get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested,
+               gralloc4::decodePixelFormatRequested, outPixelFormatRequested);
+}
+
+status_t Gralloc4Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+                                              uint32_t* outPixelFormatFourCC) const {
+    return get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC,
+               gralloc4::decodePixelFormatFourCC, outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+                                                uint64_t* outPixelFormatModifier) const {
+    return get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier,
+               gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const {
+    return get(bufferHandle, gralloc4::MetadataType_Usage, gralloc4::decodeUsage, outUsage);
+}
+
+status_t Gralloc4Mapper::getAllocationSize(buffer_handle_t bufferHandle,
+                                           uint64_t* outAllocationSize) const {
+    return get(bufferHandle, gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+               outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getProtectedContent(buffer_handle_t bufferHandle,
+                                             uint64_t* outProtectedContent) const {
+    return get(bufferHandle, gralloc4::MetadataType_ProtectedContent,
+               gralloc4::decodeProtectedContent, outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+                                        ExtendableType* outCompression) const {
+    return get(bufferHandle, gralloc4::MetadataType_Compression, gralloc4::decodeCompression,
+               outCompression);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+                                        ui::Compression* outCompression) const {
+    if (!outCompression) {
+        return BAD_VALUE;
+    }
+    ExtendableType compression;
+    status_t error = getCompression(bufferHandle, &compression);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardCompression(compression)) {
+        return BAD_TYPE;
+    }
+    *outCompression = gralloc4::getStandardCompressionValue(compression);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+                                       ExtendableType* outInterlaced) const {
+    return get(bufferHandle, gralloc4::MetadataType_Interlaced, gralloc4::decodeInterlaced,
+               outInterlaced);
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+                                       ui::Interlaced* outInterlaced) const {
+    if (!outInterlaced) {
+        return BAD_VALUE;
+    }
+    ExtendableType interlaced;
+    status_t error = getInterlaced(bufferHandle, &interlaced);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardInterlaced(interlaced)) {
+        return BAD_TYPE;
+    }
+    *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+                                         ExtendableType* outChromaSiting) const {
+    return get(bufferHandle, gralloc4::MetadataType_ChromaSiting, gralloc4::decodeChromaSiting,
+               outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+                                         ui::ChromaSiting* outChromaSiting) const {
+    if (!outChromaSiting) {
+        return BAD_VALUE;
+    }
+    ExtendableType chromaSiting;
+    status_t error = getChromaSiting(bufferHandle, &chromaSiting);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+        return BAD_TYPE;
+    }
+    *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+                                         std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+    return get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, gralloc4::decodePlaneLayouts,
+               outPlaneLayouts);
+}
+
+status_t Gralloc4Mapper::getDataspace(buffer_handle_t bufferHandle,
+                                      ui::Dataspace* outDataspace) const {
+    if (!outDataspace) {
+        return BAD_VALUE;
+    }
+    aidl::android::hardware::graphics::common::Dataspace dataspace;
+    status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
+                         &dataspace);
+    if (error) {
+        return error;
+    }
+
+    // Gralloc4 uses stable AIDL dataspace but the rest of the system still uses HIDL dataspace
+    *outDataspace = static_cast<ui::Dataspace>(dataspace);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getBlendMode(buffer_handle_t bufferHandle,
+                                      ui::BlendMode* outBlendMode) const {
+    return get(bufferHandle, gralloc4::MetadataType_BlendMode, gralloc4::decodeBlendMode,
+               outBlendMode);
+}
+
+status_t Gralloc4Mapper::getSmpte2086(buffer_handle_t bufferHandle,
+                                      std::optional<ui::Smpte2086>* outSmpte2086) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2086, gralloc4::decodeSmpte2086,
+               outSmpte2086);
+}
+
+status_t Gralloc4Mapper::getCta861_3(buffer_handle_t bufferHandle,
+                                     std::optional<ui::Cta861_3>* outCta861_3) const {
+    return get(bufferHandle, gralloc4::MetadataType_Cta861_3, gralloc4::decodeCta861_3,
+               outCta861_3);
+}
+
+status_t Gralloc4Mapper::getSmpte2094_40(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) const {
+    return get(bufferHandle, gralloc4::MetadataType_Smpte2094_40, gralloc4::decodeSmpte2094_40,
+               outSmpte2094_40);
+}
+
+template <class T>
+status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    const MetadataType& metadataType,
+                                    DecodeFunction<T> decodeFunction, T* outMetadata) const {
+    if (!outMetadata) {
+        return BAD_VALUE;
+    }
+
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, &descriptorInfo);
+
+    hidl_vec<uint8_t> vec;
+    Error error;
+    auto ret = mMapper->getFromBufferDescriptorInfo(descriptorInfo, metadataType,
+                                                    [&](const auto& tmpError,
+                                                        const hidl_vec<uint8_t>& tmpVec) {
+                                                        error = tmpError;
+                                                        vec = tmpVec;
+                                                    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("getDefault(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+              metadataType.value, error);
+        return static_cast<status_t>(error);
+    }
+
+    return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+                                                     PixelFormat format, uint32_t layerCount,
+                                                     uint64_t usage,
+                                                     uint32_t* outPixelFormatFourCC) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_PixelFormatFourCC, gralloc4::decodePixelFormatFourCC,
+                      outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+                                                       PixelFormat format, uint32_t layerCount,
+                                                       uint64_t usage,
+                                                       uint64_t* outPixelFormatModifier) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_PixelFormatModifier,
+                      gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+                                                  PixelFormat format, uint32_t layerCount,
+                                                  uint64_t usage,
+                                                  uint64_t* outAllocationSize) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+                      outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+                                                    PixelFormat format, uint32_t layerCount,
+                                                    uint64_t usage,
+                                                    uint64_t* outProtectedContent) const {
+    return getDefault(width, height, format, layerCount, usage,
+                      gralloc4::MetadataType_ProtectedContent, gralloc4::decodeProtectedContent,
+                      outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                               uint32_t layerCount, uint64_t usage,
+                                               ExtendableType* outCompression) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Compression,
+                      gralloc4::decodeCompression, outCompression);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                               uint32_t layerCount, uint64_t usage,
+                                               ui::Compression* outCompression) const {
+    if (!outCompression) {
+        return BAD_VALUE;
+    }
+    ExtendableType compression;
+    status_t error = getDefaultCompression(width, height, format, layerCount, usage, &compression);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardCompression(compression)) {
+        return BAD_TYPE;
+    }
+    *outCompression = gralloc4::getStandardCompressionValue(compression);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                              uint32_t layerCount, uint64_t usage,
+                                              ExtendableType* outInterlaced) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Interlaced,
+                      gralloc4::decodeInterlaced, outInterlaced);
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                              uint32_t layerCount, uint64_t usage,
+                                              ui::Interlaced* outInterlaced) const {
+    if (!outInterlaced) {
+        return BAD_VALUE;
+    }
+    ExtendableType interlaced;
+    status_t error = getDefaultInterlaced(width, height, format, layerCount, usage, &interlaced);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardInterlaced(interlaced)) {
+        return BAD_TYPE;
+    }
+    *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                                uint32_t layerCount, uint64_t usage,
+                                                ExtendableType* outChromaSiting) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_ChromaSiting,
+                      gralloc4::decodeChromaSiting, outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                                uint32_t layerCount, uint64_t usage,
+                                                ui::ChromaSiting* outChromaSiting) const {
+    if (!outChromaSiting) {
+        return BAD_VALUE;
+    }
+    ExtendableType chromaSiting;
+    status_t error =
+            getDefaultChromaSiting(width, height, format, layerCount, usage, &chromaSiting);
+    if (error) {
+        return error;
+    }
+    if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+        return BAD_TYPE;
+    }
+    *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+    return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultPlaneLayouts(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+    return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_PlaneLayouts,
+                      gralloc4::decodePlaneLayouts, outPlaneLayouts);
+}
+
+std::vector<MetadataTypeDescription> Gralloc4Mapper::listSupportedMetadataTypes() const {
+    hidl_vec<MetadataTypeDescription> descriptions;
+    Error error;
+    auto ret = mMapper->listSupportedMetadataTypes(
+            [&](const auto& tmpError, const auto& tmpDescriptions) {
+                error = tmpError;
+                descriptions = tmpDescriptions;
+            });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("listSupportedMetadataType() failed with %d", error);
+        return {};
+    }
+
+    return static_cast<std::vector<MetadataTypeDescription>>(descriptions);
+}
+
+template <class T>
+status_t Gralloc4Mapper::metadataDumpHelper(const BufferDump& bufferDump,
+                                            StandardMetadataType metadataType,
+                                            DecodeFunction<T> decodeFunction, T* outT) const {
+    const auto& metadataDump = bufferDump.metadataDump;
+
+    auto itr =
+            std::find_if(metadataDump.begin(), metadataDump.end(),
+                         [&](const MetadataDump& tmpMetadataDump) {
+                             if (!gralloc4::isStandardMetadataType(tmpMetadataDump.metadataType)) {
+                                 return false;
+                             }
+                             return metadataType ==
+                                     gralloc4::getStandardMetadataTypeValue(
+                                             tmpMetadataDump.metadataType);
+                         });
+    if (itr == metadataDump.end()) {
+        return BAD_VALUE;
+    }
+
+    return decodeFunction(itr->metadata, outT);
+}
+
+status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ostringstream* outDump,
+                                          uint64_t* outAllocationSize, bool less) const {
+    uint64_t bufferId;
+    std::string name;
+    uint64_t width;
+    uint64_t height;
+    uint64_t layerCount;
+    ui::PixelFormat pixelFormatRequested;
+    uint32_t pixelFormatFourCC;
+    uint64_t pixelFormatModifier;
+    uint64_t usage;
+    uint64_t allocationSize;
+    uint64_t protectedContent;
+    ExtendableType compression;
+    ExtendableType interlaced;
+    ExtendableType chromaSiting;
+    std::vector<ui::PlaneLayout> planeLayouts;
+
+    status_t error = metadataDumpHelper(bufferDump, StandardMetadataType::BUFFER_ID,
+                                        gralloc4::decodeBufferId, &bufferId);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::NAME, gralloc4::decodeName, &name);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::WIDTH, gralloc4::decodeWidth,
+                               &width);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::HEIGHT, gralloc4::decodeHeight,
+                               &height);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::LAYER_COUNT,
+                               gralloc4::decodeLayerCount, &layerCount);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+                               gralloc4::decodePixelFormatRequested, &pixelFormatRequested);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_FOURCC,
+                               gralloc4::decodePixelFormatFourCC, &pixelFormatFourCC);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_MODIFIER,
+                               gralloc4::decodePixelFormatModifier, &pixelFormatModifier);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::USAGE, gralloc4::decodeUsage,
+                               &usage);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
+                               gralloc4::decodeAllocationSize, &allocationSize);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PROTECTED_CONTENT,
+                               gralloc4::decodeProtectedContent, &protectedContent);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::COMPRESSION,
+                               gralloc4::decodeCompression, &compression);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::INTERLACED,
+                               gralloc4::decodeInterlaced, &interlaced);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::CHROMA_SITING,
+                               gralloc4::decodeChromaSiting, &chromaSiting);
+    if (error != NO_ERROR) {
+        return error;
+    }
+    error = metadataDumpHelper(bufferDump, StandardMetadataType::PLANE_LAYOUTS,
+                               gralloc4::decodePlaneLayouts, &planeLayouts);
+    if (error != NO_ERROR) {
+        return error;
+    }
+
+    if (outAllocationSize) {
+        *outAllocationSize = allocationSize;
+    }
+    double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
+
+    *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
+             << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
+             << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+             << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+             << ", compressed: ";
+
+    if (less) {
+        bool isCompressed = !gralloc4::isStandardCompression(compression) ||
+                (gralloc4::getStandardCompressionValue(compression) != ui::Compression::NONE);
+        *outDump << std::boolalpha << isCompressed << "\n";
+    } else {
+        *outDump << gralloc4::getCompressionName(compression) << "\n";
+    }
+
+    bool firstPlane = true;
+    for (const auto& planeLayout : planeLayouts) {
+        if (firstPlane) {
+            firstPlane = false;
+            *outDump << "\tplanes: ";
+        } else {
+            *outDump << "\t        ";
+        }
+
+        for (size_t i = 0; i < planeLayout.components.size(); i++) {
+            const auto& planeLayoutComponent = planeLayout.components[i];
+            *outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+            if (i < planeLayout.components.size() - 1) {
+                *outDump << "/";
+            } else {
+                *outDump << ":\t";
+            }
+        }
+        *outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+                 << ", stride:" << planeLayout.strideInBytes
+                 << " bytes, size:" << planeLayout.totalSizeInBytes;
+        if (!less) {
+            *outDump << ", inc:" << planeLayout.sampleIncrementInBits
+                     << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+                     << planeLayout.verticalSubsampling;
+        }
+        *outDump << "\n";
+    }
+
+    if (!less) {
+        *outDump << "\tlayer cnt: " << layerCount << ", protected content: " << protectedContent
+                 << ", interlaced: " << gralloc4::getInterlacedName(interlaced)
+                 << ", chroma siting:" << gralloc4::getChromaSitingName(chromaSiting) << "\n";
+    }
+
+    return NO_ERROR;
+}
+
+std::string Gralloc4Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
+    auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+    BufferDump bufferDump;
+    Error error;
+    auto ret = mMapper->dumpBuffer(buffer, [&](const auto& tmpError, const auto& tmpBufferDump) {
+        error = tmpError;
+        bufferDump = tmpBufferDump;
+    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("dumpBuffer() failed with %d", error);
+        return "";
+    }
+
+    std::ostringstream stream;
+    stream.precision(2);
+
+    status_t err = bufferDumpHelper(bufferDump, &stream, nullptr, less);
+    if (err != NO_ERROR) {
+        ALOGE("bufferDumpHelper() failed with %d", err);
+        return "";
+    }
+
+    return stream.str();
+}
+
+std::string Gralloc4Mapper::dumpBuffers(bool less) const {
+    hidl_vec<BufferDump> bufferDumps;
+    Error error;
+    auto ret = mMapper->dumpBuffers([&](const auto& tmpError, const auto& tmpBufferDump) {
+        error = tmpError;
+        bufferDumps = tmpBufferDump;
+    });
+
+    if (!ret.isOk()) {
+        error = kTransactionError;
+    }
+
+    if (error != Error::NONE) {
+        ALOGE("dumpBuffer() failed with %d", error);
+        return "";
+    }
+
+    uint64_t totalAllocationSize = 0;
+    std::ostringstream stream;
+    stream.precision(2);
+
+    stream << "Imported gralloc buffers:\n";
+
+    for (const auto& bufferDump : bufferDumps) {
+        uint64_t allocationSize = 0;
+        status_t err = bufferDumpHelper(bufferDump, &stream, &allocationSize, less);
+        if (err != NO_ERROR) {
+            ALOGE("bufferDumpHelper() failed with %d", err);
+            return "";
+        }
+        totalAllocationSize += allocationSize;
+    }
+
+    double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+    stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+    return stream.str();
+}
+
+Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
+    mAllocator = IAllocator::getService();
+    if (mAllocator == nullptr) {
+        ALOGW("allocator 3.x is not supported");
+        return;
+    }
+}
+
+bool Gralloc4Allocator::isLoaded() const {
+    return mAllocator != nullptr;
+}
+
+std::string Gralloc4Allocator::dumpDebugInfo(bool less) const {
+    return mMapper.dumpBuffers(less);
+}
+
+status_t Gralloc4Allocator::allocate(std::string requestorName, 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, bool importBuffers) const {
+    IMapper::BufferDescriptorInfo descriptorInfo;
+    sBufferDescriptorInfo(requestorName, 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;
+                                        }
+
+                                        if (importBuffers) {
+                                            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;
+                                                }
+                                            }
+                                        } else {
+                                            for (uint32_t i = 0; i < bufferCount; i++) {
+                                                outBufferHandles[i] = native_handle_clone(
+                                                        tmpBuffers[i].getNativeHandle());
+                                                if (!outBufferHandles[i]) {
+                                                    for (uint32_t j = 0; j < i; j++) {
+                                                        auto buffer = const_cast<native_handle_t*>(
+                                                                outBufferHandles[j]);
+                                                        native_handle_close(buffer);
+                                                        native_handle_delete(buffer);
+                                                        outBufferHandles[j] = nullptr;
+                                                    }
+                                                }
+                                            }
+                                        }
+                                        *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 3fc6a2d..05fc590 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -27,7 +27,6 @@
 #include <ui/BufferHubBuffer.h>
 #endif // LIBUI_IN_VNDK
 
-#include <ui/Gralloc2.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
 #include <utils/Trace.h>
@@ -626,7 +625,7 @@
                            bufferHubBuffer->desc().layers, bufferHubBuffer->desc().usage,
                            bufferHubBuffer->desc().stride);
     mBufferId = bufferHubBuffer->id();
-    mBufferHubBuffer.reset(std::move(bufferHubBuffer.get()));
+    mBufferHubBuffer = std::move(bufferHubBuffer);
 
     return NO_ERROR;
 }
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index b54ce0f..943d13e 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -33,6 +33,7 @@
 #include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
 #include <ui/Gralloc3.h>
+#include <ui/Gralloc4.h>
 #include <ui/GraphicBufferMapper.h>
 
 namespace android {
@@ -47,16 +48,23 @@
     GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList;
 
 GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) {
+    mAllocator = std::make_unique<const Gralloc4Allocator>(
+            reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper()));
+    if (mAllocator->isLoaded()) {
+        return;
+    }
     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()) {
+        return;
+    }
+    mAllocator = std::make_unique<const Gralloc2Allocator>(
+            reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper()));
+    if (mAllocator->isLoaded()) {
+        return;
     }
 
-    if (!mAllocator->isLoaded()) {
-        LOG_ALWAYS_FATAL("gralloc-allocator is missing");
-    }
+    LOG_ALWAYS_FATAL("gralloc-allocator is missing");
 }
 
 GraphicBufferAllocator::~GraphicBufferAllocator() {}
@@ -70,11 +78,11 @@
     return total;
 }
 
-void GraphicBufferAllocator::dump(std::string& result) const {
+void GraphicBufferAllocator::dump(std::string& result, bool less) const {
     Mutex::Autolock _l(sLock);
     KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
     uint64_t total = 0;
-    result.append("Allocated buffers:\n");
+    result.append("GraphicBufferAllocator buffers:\n");
     const size_t c = list.size();
     for (size_t i=0 ; i<c ; i++) {
         const alloc_rec_t& rec(list.valueAt(i));
@@ -91,23 +99,22 @@
         }
         total += rec.size;
     }
-    StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", static_cast<double>(total) / 1024.0);
+    StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
+                  static_cast<double>(total) / 1024.0);
 
-    result.append(mAllocator->dumpDebugInfo());
+    result.append(mAllocator->dumpDebugInfo(less));
 }
 
-void GraphicBufferAllocator::dumpToSystemLog()
-{
+void GraphicBufferAllocator::dumpToSystemLog(bool less) {
     std::string s;
-    GraphicBufferAllocator::getInstance().dump(s);
+    GraphicBufferAllocator::getInstance().dump(s, less);
     ALOGD("%s", s.c_str());
 }
 
-status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
-        PixelFormat format, uint32_t layerCount, uint64_t usage,
-        buffer_handle_t* handle, uint32_t* stride,
-        uint64_t /*graphicBufferId*/, std::string requestorName)
-{
+status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
+                                                uint32_t layerCount, uint64_t usage,
+                                                buffer_handle_t* handle, uint32_t* stride,
+                                                std::string requestorName, bool importBuffer) {
     ATRACE_CALL();
 
     // make sure to not allocate a N x 0 or 0 x N buffer, since this is
@@ -130,8 +137,18 @@
     // TODO(b/72323293, b/72703005): Remove these invalid bits from callers
     usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
 
-    status_t error =
-            mAllocator->allocate(width, height, format, layerCount, usage, 1, stride, handle);
+    status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
+                                          1, stride, handle, importBuffer);
+    if (error != NO_ERROR) {
+        ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
+              "usage %" PRIx64 ": %d",
+              width, height, layerCount, format, usage, error);
+        return NO_MEMORY;
+    }
+
+    if (!importBuffer) {
+        return NO_ERROR;
+    }
     size_t bufSize;
 
     // if stride has no meaning or is too large,
@@ -143,28 +160,44 @@
         bufSize = static_cast<size_t>((*stride)) * height * bpp;
     }
 
-    if (error == NO_ERROR) {
-        Mutex::Autolock _l(sLock);
-        KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
-        alloc_rec_t rec;
-        rec.width = width;
-        rec.height = height;
-        rec.stride = *stride;
-        rec.format = format;
-        rec.layerCount = layerCount;
-        rec.usage = usage;
-        rec.size = bufSize;
-        rec.requestorName = std::move(requestorName);
-        list.add(*handle, rec);
+    Mutex::Autolock _l(sLock);
+    KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+    alloc_rec_t rec;
+    rec.width = width;
+    rec.height = height;
+    rec.stride = *stride;
+    rec.format = format;
+    rec.layerCount = layerCount;
+    rec.usage = usage;
+    rec.size = bufSize;
+    rec.requestorName = std::move(requestorName);
+    list.add(*handle, rec);
 
-        return NO_ERROR;
-    } else {
-        ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
-                "usage %" PRIx64 ": %d",
-                width, height, layerCount, format, usage,
-                error);
-        return NO_MEMORY;
-    }
+    return NO_ERROR;
+}
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          buffer_handle_t* handle, uint32_t* stride,
+                                          std::string requestorName) {
+    return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+                          true);
+}
+
+status_t GraphicBufferAllocator::allocateRawHandle(uint32_t width, uint32_t height,
+                                                   PixelFormat format, uint32_t layerCount,
+                                                   uint64_t usage, buffer_handle_t* handle,
+                                                   uint32_t* stride, std::string requestorName) {
+    return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+                          false);
+}
+
+// DEPRECATED
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          buffer_handle_t* handle, uint32_t* stride,
+                                          uint64_t /*graphicBufferId*/, std::string requestorName) {
+    return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+                          true);
 }
 
 status_t GraphicBufferAllocator::free(buffer_handle_t handle)
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 25b7247..d20bd7a 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -35,6 +35,7 @@
 #include <ui/Gralloc.h>
 #include <ui/Gralloc2.h>
 #include <ui/Gralloc3.h>
+#include <ui/Gralloc4.h>
 #include <ui/GraphicBuffer.h>
 
 #include <system/graphics.h>
@@ -47,20 +48,38 @@
 void GraphicBufferMapper::preloadHal() {
     Gralloc2Mapper::preload();
     Gralloc3Mapper::preload();
+    Gralloc4Mapper::preload();
 }
 
 GraphicBufferMapper::GraphicBufferMapper() {
+    mMapper = std::make_unique<const Gralloc4Mapper>();
+    if (mMapper->isLoaded()) {
+        mMapperVersion = Version::GRALLOC_4;
+        return;
+    }
     mMapper = std::make_unique<const Gralloc3Mapper>();
-    if (!mMapper->isLoaded()) {
-        mMapper = std::make_unique<const Gralloc2Mapper>();
-        mMapperVersion = Version::GRALLOC_2;
-    } else {
+    if (mMapper->isLoaded()) {
         mMapperVersion = Version::GRALLOC_3;
+        return;
+    }
+    mMapper = std::make_unique<const Gralloc2Mapper>();
+    if (mMapper->isLoaded()) {
+        mMapperVersion = Version::GRALLOC_2;
+        return;
     }
 
-    if (!mMapper->isLoaded()) {
-        LOG_ALWAYS_FATAL("gralloc-mapper is missing");
-    }
+    LOG_ALWAYS_FATAL("gralloc-mapper is missing");
+}
+
+void GraphicBufferMapper::dumpBuffer(buffer_handle_t bufferHandle, std::string& result,
+                                     bool less) const {
+    result.append(mMapper->dumpBuffer(bufferHandle, less));
+}
+
+void GraphicBufferMapper::dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less) {
+    std::string s;
+    GraphicBufferMapper::getInstance().dumpBuffer(bufferHandle, s, less);
+    ALOGD("%s", s.c_str());
 }
 
 status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
@@ -169,5 +188,197 @@
                                           uint64_t usage, bool* outSupported) {
     return mMapper->isSupported(width, height, format, layerCount, usage, outSupported);
 }
+
+status_t GraphicBufferMapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) {
+    return mMapper->getBufferId(bufferHandle, outBufferId);
+}
+
+status_t GraphicBufferMapper::getName(buffer_handle_t bufferHandle, std::string* outName) {
+    return mMapper->getName(bufferHandle, outName);
+}
+
+status_t GraphicBufferMapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) {
+    return mMapper->getWidth(bufferHandle, outWidth);
+}
+
+status_t GraphicBufferMapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) {
+    return mMapper->getHeight(bufferHandle, outHeight);
+}
+
+status_t GraphicBufferMapper::getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) {
+    return mMapper->getLayerCount(bufferHandle, outLayerCount);
+}
+
+status_t GraphicBufferMapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                                      ui::PixelFormat* outPixelFormatRequested) {
+    return mMapper->getPixelFormatRequested(bufferHandle, outPixelFormatRequested);
+}
+
+status_t GraphicBufferMapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+                                                   uint32_t* outPixelFormatFourCC) {
+    return mMapper->getPixelFormatFourCC(bufferHandle, outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+                                                     uint64_t* outPixelFormatModifier) {
+    return mMapper->getPixelFormatModifier(bufferHandle, outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) {
+    return mMapper->getUsage(bufferHandle, outUsage);
+}
+
+status_t GraphicBufferMapper::getAllocationSize(buffer_handle_t bufferHandle,
+                                                uint64_t* outAllocationSize) {
+    return mMapper->getAllocationSize(bufferHandle, outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getProtectedContent(buffer_handle_t bufferHandle,
+                                                  uint64_t* outProtectedContent) {
+    return mMapper->getProtectedContent(bufferHandle, outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getCompression(
+        buffer_handle_t bufferHandle,
+        aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+    return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getCompression(buffer_handle_t bufferHandle,
+                                             ui::Compression* outCompression) {
+    return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getInterlaced(
+        buffer_handle_t bufferHandle,
+        aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+    return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getInterlaced(buffer_handle_t bufferHandle,
+                                            ui::Interlaced* outInterlaced) {
+    return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(
+        buffer_handle_t bufferHandle,
+        aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+    return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(buffer_handle_t bufferHandle,
+                                              ui::ChromaSiting* outChromaSiting) {
+    return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+                                              std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+    return mMapper->getPlaneLayouts(bufferHandle, outPlaneLayouts);
+}
+
+status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle,
+                                           ui::Dataspace* outDataspace) {
+    return mMapper->getDataspace(bufferHandle, outDataspace);
+}
+
+status_t GraphicBufferMapper::getBlendMode(buffer_handle_t bufferHandle,
+                                           ui::BlendMode* outBlendMode) {
+    return mMapper->getBlendMode(bufferHandle, outBlendMode);
+}
+
+status_t GraphicBufferMapper::getSmpte2086(buffer_handle_t bufferHandle,
+                                           std::optional<ui::Smpte2086>* outSmpte2086) {
+    return mMapper->getSmpte2086(bufferHandle, outSmpte2086);
+}
+
+status_t GraphicBufferMapper::getCta861_3(buffer_handle_t bufferHandle,
+                                          std::optional<ui::Cta861_3>* outCta861_3) {
+    return mMapper->getCta861_3(bufferHandle, outCta861_3);
+}
+
+status_t GraphicBufferMapper::getSmpte2094_40(
+        buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+    return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+                                                          PixelFormat format, uint32_t layerCount,
+                                                          uint64_t usage,
+                                                          uint32_t* outPixelFormatFourCC) {
+    return mMapper->getDefaultPixelFormatFourCC(width, height, format, layerCount, usage,
+                                                outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+                                                            PixelFormat format, uint32_t layerCount,
+                                                            uint64_t usage,
+                                                            uint64_t* outPixelFormatModifier) {
+    return mMapper->getDefaultPixelFormatModifier(width, height, format, layerCount, usage,
+                                                  outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+                                                       PixelFormat format, uint32_t layerCount,
+                                                       uint64_t usage,
+                                                       uint64_t* outAllocationSize) {
+    return mMapper->getDefaultAllocationSize(width, height, format, layerCount, usage,
+                                             outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+                                                         PixelFormat format, uint32_t layerCount,
+                                                         uint64_t usage,
+                                                         uint64_t* outProtectedContent) {
+    return mMapper->getDefaultProtectedContent(width, height, format, layerCount, usage,
+                                               outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+    return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(uint32_t width, uint32_t height,
+                                                    PixelFormat format, uint32_t layerCount,
+                                                    uint64_t usage,
+                                                    ui::Compression* outCompression) {
+    return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+    return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(uint32_t width, uint32_t height,
+                                                   PixelFormat format, uint32_t layerCount,
+                                                   uint64_t usage, ui::Interlaced* outInterlaced) {
+    return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+    return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+                                           outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(uint32_t width, uint32_t height,
+                                                     PixelFormat format, uint32_t layerCount,
+                                                     uint64_t usage,
+                                                     ui::ChromaSiting* outChromaSiting) {
+    return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+                                           outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultPlaneLayouts(
+        uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+        std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+    return mMapper->getDefaultPlaneLayouts(width, height, format, layerCount, usage,
+                                           outPlaneLayouts);
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 1222cd6..cd2a448 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -23,11 +23,10 @@
 
 #include <utils/Log.h>
 
+#include <ui/Point.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
-#include <ui/Point.h>
-
-#include <private/ui/RegionHelper.h>
+#include <ui/RegionHelper.h>
 
 // ----------------------------------------------------------------------------
 
@@ -68,19 +67,20 @@
 // ----------------------------------------------------------------------------
 
 Region::Region() {
-    mStorage.add(Rect(0,0));
+    mStorage.push_back(Rect(0, 0));
 }
 
 Region::Region(const Region& rhs)
-    : mStorage(rhs.mStorage)
 {
+    mStorage.clear();
+    mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
 #if defined(VALIDATE_REGIONS)
     validate(rhs, "rhs copy-ctor");
 #endif
 }
 
 Region::Region(const Rect& rhs) {
-    mStorage.add(rhs);
+    mStorage.push_back(rhs);
 }
 
 Region::~Region()
@@ -101,8 +101,8 @@
  * final, correctly ordered region buffer. Each rectangle will be compared with the span directly
  * above it, and subdivided to resolve any remaining T-junctions.
  */
-static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
-        Vector<Rect>& dst, int spanDirection) {
+static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector<Rect>& dst,
+                                           int spanDirection) {
     dst.clear();
 
     const Rect* current = end - 1;
@@ -110,7 +110,7 @@
 
     // add first span immediately
     do {
-        dst.add(*current);
+        dst.push_back(*current);
         current--;
     } while (current->top == lastTop && current >= begin);
 
@@ -148,12 +148,12 @@
                 if (prev.right <= left) break;
 
                 if (prev.right > left && prev.right < right) {
-                    dst.add(Rect(prev.right, top, right, bottom));
+                    dst.push_back(Rect(prev.right, top, right, bottom));
                     right = prev.right;
                 }
 
                 if (prev.left > left && prev.left < right) {
-                    dst.add(Rect(prev.left, top, right, bottom));
+                    dst.push_back(Rect(prev.left, top, right, bottom));
                     right = prev.left;
                 }
 
@@ -167,12 +167,12 @@
                 if (prev.left >= right) break;
 
                 if (prev.left > left && prev.left < right) {
-                    dst.add(Rect(left, top, prev.left, bottom));
+                    dst.push_back(Rect(left, top, prev.left, bottom));
                     left = prev.left;
                 }
 
                 if (prev.right > left && prev.right < right) {
-                    dst.add(Rect(left, top, prev.right, bottom));
+                    dst.push_back(Rect(left, top, prev.right, bottom));
                     left = prev.right;
                 }
                 // if an entry in the previous span is too far left, nothing further right in the
@@ -184,7 +184,7 @@
         }
 
         if (left < right) {
-            dst.add(Rect(left, top, right, bottom));
+            dst.push_back(Rect(left, top, right, bottom));
         }
 
         current--;
@@ -202,13 +202,14 @@
     if (r.isEmpty()) return r;
     if (r.isRect()) return r;
 
-    Vector<Rect> reversed;
+    FatVector<Rect> reversed;
     reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
 
     Region outputRegion;
-    reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
-            outputRegion.mStorage, direction_LTR);
-    outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
+    reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(),
+                                   outputRegion.mStorage, direction_LTR);
+    outputRegion.mStorage.push_back(
+            r.getBounds()); // to make region valid, mStorage must end with bounds
 
 #if defined(VALIDATE_REGIONS)
     validate(outputRegion, "T-Junction free region");
@@ -223,7 +224,8 @@
     validate(*this, "this->operator=");
     validate(rhs, "rhs.operator=");
 #endif
-    mStorage = rhs.mStorage;
+    mStorage.clear();
+    mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
     return *this;
 }
 
@@ -232,7 +234,7 @@
     if (mStorage.size() >= 2) {
         const Rect bounds(getBounds());
         mStorage.clear();
-        mStorage.add(bounds);
+        mStorage.push_back(bounds);
     }
     return *this;
 }
@@ -256,38 +258,51 @@
 void Region::clear()
 {
     mStorage.clear();
-    mStorage.add(Rect(0,0));
+    mStorage.push_back(Rect(0, 0));
 }
 
 void Region::set(const Rect& r)
 {
     mStorage.clear();
-    mStorage.add(r);
+    mStorage.push_back(r);
 }
 
 void Region::set(int32_t w, int32_t h)
 {
     mStorage.clear();
-    mStorage.add(Rect(w, h));
+    mStorage.push_back(Rect(w, h));
 }
 
 void Region::set(uint32_t w, uint32_t h)
 {
     mStorage.clear();
-    mStorage.add(Rect(w, h));
+    mStorage.push_back(Rect(w, h));
 }
 
 bool Region::isTriviallyEqual(const Region& region) const {
     return begin() == region.begin();
 }
 
+bool Region::hasSameRects(const Region& other) const {
+    size_t thisRectCount = 0;
+    android::Rect const* thisRects = getArray(&thisRectCount);
+    size_t otherRectCount = 0;
+    android::Rect const* otherRects = other.getArray(&otherRectCount);
+
+    if (thisRectCount != otherRectCount) return false;
+
+    for (size_t i = 0; i < thisRectCount; i++) {
+        if (thisRects[i] != otherRects[i]) return false;
+    }
+    return true;
+}
+
 // ----------------------------------------------------------------------------
 
 void Region::addRectUnchecked(int l, int t, int r, int b)
 {
     Rect rect(l,t,r,b);
-    size_t where = mStorage.size() - 1;
-    mStorage.insertAt(rect, where, 1);
+    mStorage.insert(mStorage.end() - 1, rect);
 }
 
 // ----------------------------------------------------------------------------
@@ -337,7 +352,7 @@
 
 Region& Region::scaleSelf(float sx, float sy) {
     size_t count = mStorage.size();
-    Rect* rects = mStorage.editArray();
+    Rect* rects = mStorage.data();
     while (count) {
         rects->left = static_cast<int32_t>(static_cast<float>(rects->left) * sx + 0.5f);
         rects->right = static_cast<int32_t>(static_cast<float>(rects->right) * sx + 0.5f);
@@ -442,10 +457,10 @@
 class Region::rasterizer : public region_operator<Rect>::region_rasterizer
 {
     Rect bounds;
-    Vector<Rect>& storage;
+    FatVector<Rect>& storage;
     Rect* head;
     Rect* tail;
-    Vector<Rect> span;
+    FatVector<Rect> span;
     Rect* cur;
 public:
     explicit rasterizer(Region& reg)
@@ -472,8 +487,8 @@
         flushSpan();
     }
     if (storage.size()) {
-        bounds.top = storage.itemAt(0).top;
-        bounds.bottom = storage.top().bottom;
+        bounds.top = storage.front().top;
+        bounds.bottom = storage.back().bottom;
         if (storage.size() == 1) {
             storage.clear();
         }
@@ -481,7 +496,7 @@
         bounds.left  = 0;
         bounds.right = 0;
     }
-    storage.add(bounds);
+    storage.push_back(bounds);
 }
 
 void Region::rasterizer::operator()(const Rect& rect)
@@ -496,15 +511,15 @@
             return;
         }
     }
-    span.add(rect);
-    cur = span.editArray() + (span.size() - 1);
+    span.push_back(rect);
+    cur = span.data() + (span.size() - 1);
 }
 
 void Region::rasterizer::flushSpan()
 {
     bool merge = false;
     if (tail-head == ssize_t(span.size())) {
-        Rect const* p = span.editArray();
+        Rect const* p = span.data();
         Rect const* q = head;
         if (p->top == q->bottom) {
             merge = true;
@@ -519,17 +534,17 @@
         }
     }
     if (merge) {
-        const int bottom = span[0].bottom;
+        const int bottom = span.front().bottom;
         Rect* r = head;
         while (r != tail) {
             r->bottom = bottom;
             r++;
         }
     } else {
-        bounds.left = min(span.itemAt(0).left, bounds.left);
-        bounds.right = max(span.top().right, bounds.right);
-        storage.appendVector(span);
-        tail = storage.editArray() + storage.size();
+        bounds.left = min(span.front().left, bounds.left);
+        bounds.right = max(span.back().right, bounds.right);
+        storage.insert(storage.end(), span.begin(), span.end());
+        tail = storage.data() + storage.size();
         head = tail - span.size();
     }
     span.clear();
@@ -537,7 +552,7 @@
 
 bool Region::validate(const Region& reg, const char* name, bool silent)
 {
-    if (reg.mStorage.isEmpty()) {
+    if (reg.mStorage.empty()) {
         ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name);
         // return immediately as the code below assumes mStorage is non-empty
         return false;
@@ -676,9 +691,8 @@
     }
     sk_dst.op(sk_lhs, sk_rhs, sk_op);
 
-    if (sk_dst.isEmpty() && dst.isEmpty())
-        return;
-    
+    if (sk_dst.empty() && dst.empty()) return;
+
     bool same = true;
     Region::const_iterator head = dst.begin();
     Region::const_iterator const tail = dst.end();
@@ -773,7 +787,7 @@
         validate(reg, "translate (before)");
 #endif
         size_t count = reg.mStorage.size();
-        Rect* rects = reg.mStorage.editArray();
+        Rect* rects = reg.mStorage.data();
         while (count) {
             rects->offsetBy(dx, dy);
             rects++;
@@ -853,24 +867,25 @@
         ALOGE("Region::unflatten() failed, invalid region");
         return BAD_VALUE;
     }
-    mStorage = result.mStorage;
+    mStorage.clear();
+    mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end());
     return NO_ERROR;
 }
 
 // ----------------------------------------------------------------------------
 
 Region::const_iterator Region::begin() const {
-    return mStorage.array();
+    return mStorage.data();
 }
 
 Region::const_iterator Region::end() const {
     // Workaround for b/77643177
     // mStorage should never be empty, but somehow it is and it's causing
     // an abort in ubsan
-    if (mStorage.isEmpty()) return mStorage.array();
+    if (mStorage.empty()) return mStorage.data();
 
     size_t numRects = isRect() ? 1 : mStorage.size() - 1;
-    return mStorage.array() + numRects;
+    return mStorage.data() + numRects;
 }
 
 Rect const* Region::getArray(size_t* count) const {
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 28c3f7b..06b6bfe 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -33,8 +33,8 @@
     : mMatrix(other.mMatrix), mType(other.mType) {
 }
 
-Transform::Transform(uint32_t orientation) {
-    set(orientation, 0, 0);
+Transform::Transform(uint32_t orientation, int w, int h) {
+    set(orientation, w, h);
 }
 
 Transform::~Transform() = default;
@@ -49,6 +49,15 @@
     return isZero(fabs(f) - 1.0f);
 }
 
+bool Transform::operator==(const Transform& other) const {
+    return mMatrix[0][0] == other.mMatrix[0][0] && mMatrix[0][1] == other.mMatrix[0][1] &&
+            mMatrix[0][2] == other.mMatrix[0][2] && mMatrix[1][0] == other.mMatrix[1][0] &&
+            mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
+            mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
+            mMatrix[2][2] == other.mMatrix[2][2];
+    ;
+}
+
 Transform Transform::operator * (const Transform& rhs) const
 {
     if (CC_LIKELY(mType == IDENTITY))
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h b/libs/ui/include/ui/DisplayConfig.h
similarity index 62%
rename from services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
rename to libs/ui/include/ui/DisplayConfig.h
index ab01c20..09b8211 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h
+++ b/libs/ui/include/ui/DisplayConfig.h
@@ -16,26 +16,25 @@
 
 #pragma once
 
-#include <cstdint>
-#include <string>
+#include <type_traits>
 
-#include <compositionengine/LayerFECompositionState.h>
-#include <renderengine/Mesh.h>
+#include <ui/Size.h>
+#include <utils/Timers.h>
 
 namespace android {
 
-namespace compositionengine::impl {
+// Configuration supported by physical display.
+struct DisplayConfig {
+    ui::Size resolution;
+    float xDpi = 0;
+    float yDpi = 0;
 
-struct LayerCompositionState {
-    /*
-     * State intended to be set by LayerFE::getCompositionState
-     */
-
-    LayerFECompositionState frontEnd;
-
-    // Debugging
-    void dump(std::string& result) const;
+    float refreshRate = 0;
+    nsecs_t appVsyncOffset = 0;
+    nsecs_t sfVsyncOffset = 0;
+    nsecs_t presentationDeadline = 0;
 };
 
-} // namespace compositionengine::impl
+static_assert(std::is_trivially_copyable_v<DisplayConfig>);
+
 } // namespace android
diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h
index 8976d2d..69f86d3 100644
--- a/libs/ui/include/ui/DisplayInfo.h
+++ b/libs/ui/include/ui/DisplayInfo.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 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,39 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_UI_DISPLAY_INFO_H
-#define ANDROID_UI_DISPLAY_INFO_H
+#pragma once
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Timers.h>
+#include <type_traits>
 
 namespace android {
 
+enum class DisplayConnectionType { Internal, External };
+
+// Immutable information about physical display.
 struct DisplayInfo {
-    uint32_t w{0};
-    uint32_t h{0};
-    float xdpi{0};
-    float ydpi{0};
-    float fps{0};
-    float density{0};
-    uint8_t orientation{0};
-    bool secure{false};
-    nsecs_t appVsyncOffset{0};
-    nsecs_t presentationDeadline{0};
-    uint32_t viewportW{0};
-    uint32_t viewportH{0};
+    DisplayConnectionType connectionType = DisplayConnectionType::Internal;
+    float density = 0.f;
+    bool secure = false;
 };
 
-/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */
-enum {
-    DISPLAY_ORIENTATION_0 = 0,
-    DISPLAY_ORIENTATION_90 = 1,
-    DISPLAY_ORIENTATION_180 = 2,
-    DISPLAY_ORIENTATION_270 = 3
-};
+static_assert(std::is_trivially_copyable_v<DisplayInfo>);
 
-}; // namespace android
-
-#endif // ANDROID_COMPOSER_DISPLAY_INFO_H
+} // namespace android
diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h
new file mode 100644
index 0000000..64efc84
--- /dev/null
+++ b/libs/ui/include/ui/DisplayState.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <ui/Rotation.h>
+#include <ui/Size.h>
+
+#include <cstdint>
+#include <type_traits>
+
+namespace android::ui {
+
+using LayerStack = uint32_t;
+constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1);
+
+// Transactional state of physical or virtual display. Note that libgui defines
+// android::DisplayState as a superset of android::ui::DisplayState.
+struct DisplayState {
+    LayerStack layerStack = NO_LAYER_STACK;
+    Rotation orientation = ROTATION_0;
+    Size viewport;
+};
+
+static_assert(std::is_trivially_copyable_v<DisplayState>);
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
new file mode 100644
index 0000000..25fe3a0
--- /dev/null
+++ b/libs/ui/include/ui/FatVector.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_REGION_FAT_VECTOR_H
+#define ANDROID_REGION_FAT_VECTOR_H
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <utils/Log.h>
+#include <type_traits>
+
+#include <vector>
+
+namespace android {
+
+template <typename T, size_t SIZE = 4>
+class InlineStdAllocator {
+public:
+    struct Allocation {
+    private:
+        Allocation(const Allocation&) = delete;
+        void operator=(const Allocation&) = delete;
+
+    public:
+        Allocation() {}
+        // char array instead of T array, so memory is uninitialized, with no destructors run
+        char array[sizeof(T) * SIZE];
+        bool inUse = false;
+    };
+
+    typedef T value_type; // needed to implement std::allocator
+    typedef T* pointer;   // needed to implement std::allocator
+
+    explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
+    InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
+    ~InlineStdAllocator() {}
+
+    T* allocate(size_t num, const void* = 0) {
+        if (!mAllocation.inUse && num <= SIZE) {
+            mAllocation.inUse = true;
+            return static_cast<T*>(static_cast<void*>(mAllocation.array));
+        } else {
+            return static_cast<T*>(static_cast<void*>(malloc(num * sizeof(T))));
+        }
+    }
+
+    void deallocate(pointer p, size_t) {
+        if (p == static_cast<T*>(static_cast<void*>(mAllocation.array))) {
+            mAllocation.inUse = false;
+        } else {
+            // 'free' instead of delete here - destruction handled separately
+            free(p);
+        }
+    }
+    Allocation& mAllocation;
+};
+
+/**
+ * std::vector with SIZE elements preallocated into an internal buffer.
+ *
+ * Useful for avoiding the cost of malloc in cases where only SIZE or
+ * fewer elements are needed in the common case.
+ */
+template <typename T, size_t SIZE = 4>
+class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
+public:
+    FatVector()
+          : std::vector<T, InlineStdAllocator<T, SIZE>>(InlineStdAllocator<T, SIZE>(mAllocation)) {
+        this->reserve(SIZE);
+    }
+
+    explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
+
+private:
+    typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
+};
+
+} // namespace android
+
+#endif // ANDROID_REGION_FAT_VECTOR_H
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 4cd9a0b..bec2552 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <ostream>
+
 namespace android {
 
 class FloatRect {
@@ -52,4 +54,9 @@
     return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
 }
 
+static inline void PrintTo(const FloatRect& rect, ::std::ostream* os) {
+    *os << "FloatRect(" << rect.left << ", " << rect.top << ", " << rect.right << ", "
+        << rect.bottom << ")";
+}
+
 }  // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 6cc23f0..e199648 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -17,13 +17,15 @@
 #ifndef ANDROID_UI_GRALLOC_H
 #define ANDROID_UI_GRALLOC_H
 
-#include <string>
-
+#include <gralloctypes/Gralloc4.h>
 #include <hidl/HidlSupport.h>
+#include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
 #include <utils/StrongPointer.h>
 
+#include <string>
+
 namespace android {
 
 // A wrapper to IMapper
@@ -33,6 +35,10 @@
 
     virtual bool isLoaded() const = 0;
 
+    virtual std::string dumpBuffer(buffer_handle_t /*bufferHandle*/, bool /*less*/) const {
+        return "";
+    }
+
     virtual status_t createDescriptor(void* bufferDescriptorInfo,
                                       void* outBufferDescriptor) const = 0;
 
@@ -74,8 +80,176 @@
     // allocated if resources are available.  If false, a buffer with the given specifications will
     // never successfully allocate on this device. Note that this function is not guaranteed to be
     // supported on all devices, in which case a status_t of INVALID_OPERATION will be returned.
-    virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
-                                 uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0;
+    virtual status_t isSupported(uint32_t /*width*/, uint32_t /*height*/,
+                                 android::PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                 uint64_t /*usage*/, bool* /*outSupported*/) const {
+        return INVALID_OPERATION;
+    }
+
+    virtual status_t getBufferId(buffer_handle_t /*bufferHandle*/,
+                                 uint64_t* /*outBufferId*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getName(buffer_handle_t /*bufferHandle*/, std::string* /*outName*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getWidth(buffer_handle_t /*bufferHandle*/, uint64_t* /*outWidth*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getHeight(buffer_handle_t /*bufferHandle*/, uint64_t* /*outHeight*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getLayerCount(buffer_handle_t /*bufferHandle*/,
+                                   uint64_t* /*outLayerCount*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPixelFormatRequested(buffer_handle_t /*bufferHandle*/,
+                                             ui::PixelFormat* /*outPixelFormatRequested*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPixelFormatFourCC(buffer_handle_t /*bufferHandle*/,
+                                          uint32_t* /*outPixelFormatFourCC*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPixelFormatModifier(buffer_handle_t /*bufferHandle*/,
+                                            uint64_t* /*outPixelFormatModifier*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getUsage(buffer_handle_t /*bufferHandle*/, uint64_t* /*outUsage*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getAllocationSize(buffer_handle_t /*bufferHandle*/,
+                                       uint64_t* /*outAllocationSize*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getProtectedContent(buffer_handle_t /*bufferHandle*/,
+                                         uint64_t* /*outProtectedContent*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getCompression(
+            buffer_handle_t /*bufferHandle*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getCompression(buffer_handle_t /*bufferHandle*/,
+                                    ui::Compression* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getInterlaced(
+            buffer_handle_t /*bufferHandle*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getInterlaced(buffer_handle_t /*bufferHandle*/,
+                                   ui::Interlaced* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getChromaSiting(
+            buffer_handle_t /*bufferHandle*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getChromaSiting(buffer_handle_t /*bufferHandle*/,
+                                     ui::ChromaSiting* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getPlaneLayouts(buffer_handle_t /*bufferHandle*/,
+                                     std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDataspace(buffer_handle_t /*bufferHandle*/,
+                                  ui::Dataspace* /*outDataspace*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getBlendMode(buffer_handle_t /*bufferHandle*/,
+                                  ui::BlendMode* /*outBlendMode*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getSmpte2086(buffer_handle_t /*bufferHandle*/,
+                                  std::optional<ui::Smpte2086>* /*outSmpte2086*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getCta861_3(buffer_handle_t /*bufferHandle*/,
+                                 std::optional<ui::Cta861_3>* /*outCta861_3*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getSmpte2094_40(
+            buffer_handle_t /*bufferHandle*/,
+            std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
+        return INVALID_OPERATION;
+    }
+
+    virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
+                                                 PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                                 uint64_t /*usage*/,
+                                                 uint32_t* /*outPixelFormatFourCC*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultPixelFormatModifier(uint32_t /*width*/, uint32_t /*height*/,
+                                                   PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                                   uint64_t /*usage*/,
+                                                   uint64_t* /*outPixelFormatModifier*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultAllocationSize(uint32_t /*width*/, uint32_t /*height*/,
+                                              PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                              uint64_t /*usage*/,
+                                              uint64_t* /*outAllocationSize*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultProtectedContent(uint32_t /*width*/, uint32_t /*height*/,
+                                                PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                                uint64_t /*usage*/,
+                                                uint64_t* /*outProtectedContent*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultCompression(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultCompression(uint32_t /*width*/, uint32_t /*height*/,
+                                           PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                           uint64_t /*usage*/,
+                                           ui::Compression* /*outCompression*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultInterlaced(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultInterlaced(uint32_t /*width*/, uint32_t /*height*/,
+                                          PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                          uint64_t /*usage*/,
+                                          ui::Interlaced* /*outInterlaced*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultChromaSiting(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultChromaSiting(uint32_t /*width*/, uint32_t /*height*/,
+                                            PixelFormat /*format*/, uint32_t /*layerCount*/,
+                                            uint64_t /*usage*/,
+                                            ui::ChromaSiting* /*outChromaSiting*/) const {
+        return INVALID_OPERATION;
+    }
+    virtual status_t getDefaultPlaneLayouts(
+            uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+            uint32_t /*layerCount*/, uint64_t /*usage*/,
+            std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+        return INVALID_OPERATION;
+    }
+
+    virtual std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+    listSupportedMetadataTypes() const {
+        return {};
+    }
 };
 
 // A wrapper to IAllocator
@@ -85,16 +259,18 @@
 
     virtual bool isLoaded() const = 0;
 
-    virtual std::string dumpDebugInfo() const = 0;
+    virtual std::string dumpDebugInfo(bool less = true) 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;
+    virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                              PixelFormat format, uint32_t layerCount, uint64_t usage,
+                              uint32_t bufferCount, uint32_t* outStride,
+                              buffer_handle_t* outBufferHandles,
+                              bool importBuffers = true) const = 0;
 };
 
 } // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 948f597..f570c42 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -61,9 +61,6 @@
 
     int unlock(buffer_handle_t bufferHandle) const override;
 
-    status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
-                         uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
-
 private:
     // Determines whether the passed info is compatible with the mapper.
     status_t validateBufferDescriptorInfo(
@@ -81,11 +78,12 @@
 
     bool isLoaded() const override;
 
-    std::string dumpDebugInfo() const override;
+    std::string dumpDebugInfo(bool less = true) 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;
+    status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                      PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                      uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                      bool importBuffers = true) const override;
 
 private:
     const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index 0965f52..93a5077 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -79,11 +79,12 @@
 
     bool isLoaded() const override;
 
-    std::string dumpDebugInfo() const override;
+    std::string dumpDebugInfo(bool less = true) 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;
+    status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                      PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                      uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                      bool importBuffers = true) const override;
 
 private:
     const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
new file mode 100644
index 0000000..4729cba
--- /dev/null
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GRALLOC4_H
+#define ANDROID_UI_GRALLOC4_H
+
+#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
+#include <android/hardware/graphics/common/1.1/types.h>
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <gralloctypes/Gralloc4.h>
+#include <ui/Gralloc.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <utils/StrongPointer.h>
+
+#include <string>
+
+namespace android {
+
+class Gralloc4Mapper : public GrallocMapper {
+public:
+    static void preload();
+
+    Gralloc4Mapper();
+
+    bool isLoaded() const override;
+
+    std::string dumpBuffer(buffer_handle_t bufferHandle, bool less = true) const override;
+    std::string dumpBuffers(bool less = true) const;
+
+    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,
+                                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;
+
+    status_t isSupported(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+                         uint64_t usage, bool* outSupported) const override;
+
+    status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const override;
+    status_t getName(buffer_handle_t bufferHandle, std::string* outName) const override;
+    status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const override;
+    status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const override;
+    status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) const override;
+    status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                     ui::PixelFormat* outPixelFormatRequested) const override;
+    status_t getPixelFormatFourCC(buffer_handle_t bufferHandle,
+                                  uint32_t* outPixelFormatFourCC) const override;
+    status_t getPixelFormatModifier(buffer_handle_t bufferHandle,
+                                    uint64_t* outPixelFormatModifier) const override;
+    status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const override;
+    status_t getAllocationSize(buffer_handle_t bufferHandle,
+                               uint64_t* outAllocationSize) const override;
+    status_t getProtectedContent(buffer_handle_t bufferHandle,
+                                 uint64_t* outProtectedContent) const override;
+    status_t getCompression(buffer_handle_t bufferHandle,
+                            aidl::android::hardware::graphics::common::ExtendableType*
+                                    outCompression) const override;
+    status_t getCompression(buffer_handle_t bufferHandle,
+                            ui::Compression* outCompression) const override;
+    status_t getInterlaced(buffer_handle_t bufferHandle,
+                           aidl::android::hardware::graphics::common::ExtendableType* outInterlaced)
+            const override;
+    status_t getInterlaced(buffer_handle_t bufferHandle,
+                           ui::Interlaced* outInterlaced) const override;
+    status_t getChromaSiting(buffer_handle_t bufferHandle,
+                             aidl::android::hardware::graphics::common::ExtendableType*
+                                     outChromaSiting) const override;
+    status_t getChromaSiting(buffer_handle_t bufferHandle,
+                             ui::ChromaSiting* outChromaSiting) const override;
+    status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+                             std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+    status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) const override;
+    status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode) const override;
+    status_t getSmpte2086(buffer_handle_t bufferHandle,
+                          std::optional<ui::Smpte2086>* outSmpte2086) const override;
+    status_t getCta861_3(buffer_handle_t bufferHandle,
+                         std::optional<ui::Cta861_3>* outCta861_3) const override;
+    status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+
+    status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+                                         uint32_t layerCount, uint64_t usage,
+                                         uint32_t* outPixelFormatFourCC) const override;
+    status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+                                           uint32_t layerCount, uint64_t usage,
+                                           uint64_t* outPixelFormatModifier) const override;
+    status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+                                      uint32_t layerCount, uint64_t usage,
+                                      uint64_t* outAllocationSize) const override;
+    status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+                                        uint32_t layerCount, uint64_t usage,
+                                        uint64_t* outProtectedContent) const override;
+    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                   uint32_t layerCount, uint64_t usage,
+                                   aidl::android::hardware::graphics::common::ExtendableType*
+                                           outCompression) const override;
+    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                   uint32_t layerCount, uint64_t usage,
+                                   ui::Compression* outCompression) const override;
+    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                  uint32_t layerCount, uint64_t usage,
+                                  aidl::android::hardware::graphics::common::ExtendableType*
+                                          outInterlaced) const override;
+    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                  uint32_t layerCount, uint64_t usage,
+                                  ui::Interlaced* outInterlaced) const override;
+    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    aidl::android::hardware::graphics::common::ExtendableType*
+                                            outChromaSiting) const override;
+    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    ui::ChromaSiting* outChromaSiting) const override;
+    status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+
+    std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+    listSupportedMetadataTypes() const;
+
+private:
+    friend class GraphicBufferAllocator;
+
+    // Determines whether the passed info is compatible with the mapper.
+    status_t validateBufferDescriptorInfo(
+            hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+
+    template <class T>
+    using DecodeFunction = status_t (*)(const hardware::hidl_vec<uint8_t>& input, T* output);
+
+    template <class T>
+    status_t get(
+            buffer_handle_t bufferHandle,
+            const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+            DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+    template <class T>
+    status_t getDefault(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+            DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+    template <class T>
+    status_t metadataDumpHelper(
+            const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+            aidl::android::hardware::graphics::common::StandardMetadataType metadataType,
+            DecodeFunction<T> decodeFunction, T* outT) const;
+    status_t bufferDumpHelper(
+            const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+            std::ostringstream* outDump, uint64_t* outAllocationSize, bool less) const;
+
+    sp<hardware::graphics::mapper::V4_0::IMapper> mMapper;
+};
+
+class Gralloc4Allocator : public GrallocAllocator {
+public:
+    // An allocator relies on a mapper, and that mapper must be alive at all
+    // time.
+    Gralloc4Allocator(const Gralloc4Mapper& mapper);
+
+    bool isLoaded() const override;
+
+    std::string dumpDebugInfo(bool less = true) const override;
+
+    status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+                      PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+                      uint32_t* outStride, buffer_handle_t* outBufferHandles,
+                      bool importBuffers = true) const override;
+
+private:
+    const Gralloc4Mapper& mMapper;
+    sp<hardware::graphics::allocator::V4_0::IAllocator> mAllocator;
+};
+
+} // namespace android
+
+#endif // ANDROID_UI_GRALLOC4_H
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 0635727..3ed988c 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -1,19 +1,19 @@
 /*
-**
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ *
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_ALLOCATOR_H
 #define ANDROID_BUFFER_ALLOCATOR_H
@@ -42,6 +42,29 @@
 public:
     static inline GraphicBufferAllocator& get() { return getInstance(); }
 
+    /**
+     * Allocates and imports a gralloc buffer.
+     *
+     * The handle must be freed with GraphicBufferAllocator::free() when no longer needed.
+     */
+    status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+                      uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+                      std::string requestorName);
+
+    /**
+     * Allocates and does NOT import a gralloc buffer. Buffers cannot be used until they have
+     * been imported. This function is for advanced use cases only.
+     *
+     * The raw native handle must be freed by calling native_handle_close() followed by
+     * native_handle_delete().
+     */
+    status_t allocateRawHandle(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+                               uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+                               std::string requestorName);
+
+    /**
+     * DEPRECATED: GraphicBufferAllocator does not use the graphicBufferId.
+     */
     status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
             uint32_t layerCount, uint64_t usage,
             buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
@@ -51,8 +74,8 @@
 
     uint64_t getTotalSize() const;
 
-    void dump(std::string& res) const;
-    static void dumpToSystemLog();
+    void dump(std::string& res, bool less = true) const;
+    static void dumpToSystemLog(bool less = true);
 
 protected:
     struct alloc_rec_t {
@@ -66,6 +89,10 @@
         std::string requestorName;
     };
 
+    status_t allocateHelper(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+                            uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+                            std::string requestorName, bool importBuffer);
+
     static Mutex sLock;
     static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
 
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 2461454..837e3d8 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -22,10 +22,11 @@
 
 #include <memory>
 
+#include <ui/GraphicTypes.h>
 #include <ui/PixelFormat.h>
+#include <ui/Rect.h>
 #include <utils/Singleton.h>
 
-
 // Needed by code that still uses the GRALLOC_USAGE_* constants.
 // when/if we get rid of gralloc, we should provide aliases or fix call sites.
 #include <hardware/gralloc.h>
@@ -36,7 +37,6 @@
 // ---------------------------------------------------------------------------
 
 class GrallocMapper;
-class Rect;
 
 class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
 {
@@ -44,10 +44,14 @@
     enum Version {
         GRALLOC_2,
         GRALLOC_3,
+        GRALLOC_4,
     };
     static void preloadHal();
     static inline GraphicBufferMapper& get() { return getInstance(); }
 
+    void dumpBuffer(buffer_handle_t bufferHandle, std::string& result, bool less = true) const;
+    static void dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less = true);
+
     // The imported outHandle must be freed with freeBuffer when no longer
     // needed. rawHandle is owned by the caller.
     status_t importBuffer(buffer_handle_t rawHandle,
@@ -85,6 +89,86 @@
     status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
                          uint32_t layerCount, uint64_t usage, bool* outSupported);
 
+    /**
+     * Gets the gralloc metadata associated with the buffer.
+     *
+     * These functions are supported by gralloc 4.0+.
+     */
+    status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId);
+    status_t getName(buffer_handle_t bufferHandle, std::string* outName);
+    status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth);
+    status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight);
+    status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount);
+    status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+                                     ui::PixelFormat* outPixelFormatRequested);
+    status_t getPixelFormatFourCC(buffer_handle_t bufferHandle, uint32_t* outPixelFormatFourCC);
+    status_t getPixelFormatModifier(buffer_handle_t bufferHandle, uint64_t* outPixelFormatModifier);
+    status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage);
+    status_t getAllocationSize(buffer_handle_t bufferHandle, uint64_t* outAllocationSize);
+    status_t getProtectedContent(buffer_handle_t bufferHandle, uint64_t* outProtectedContent);
+    status_t getCompression(
+            buffer_handle_t bufferHandle,
+            aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+    status_t getCompression(buffer_handle_t bufferHandle, ui::Compression* outCompression);
+    status_t getInterlaced(
+            buffer_handle_t bufferHandle,
+            aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+    status_t getInterlaced(buffer_handle_t bufferHandle, ui::Interlaced* outInterlaced);
+    status_t getChromaSiting(
+            buffer_handle_t bufferHandle,
+            aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+    status_t getChromaSiting(buffer_handle_t bufferHandle, ui::ChromaSiting* outChromaSiting);
+    status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+                             std::vector<ui::PlaneLayout>* outPlaneLayouts);
+    status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace);
+    status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode);
+    status_t getSmpte2086(buffer_handle_t bufferHandle, std::optional<ui::Smpte2086>* outSmpte2086);
+    status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
+    status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+                             std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+    /**
+     * Gets the default metadata for a gralloc buffer allocated with the given parameters.
+     *
+     * These functions are supported by gralloc 4.0+.
+     */
+    status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+                                         uint32_t layerCount, uint64_t usage,
+                                         uint32_t* outPixelFormatFourCC);
+    status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+                                           uint32_t layerCount, uint64_t usage,
+                                           uint64_t* outPixelFormatModifier);
+    status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+                                      uint32_t layerCount, uint64_t usage,
+                                      uint64_t* outAllocationSize);
+    status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+                                        uint32_t layerCount, uint64_t usage,
+                                        uint64_t* outProtectedContent);
+    status_t getDefaultCompression(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+    status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+                                   uint32_t layerCount, uint64_t usage,
+                                   ui::Compression* outCompression);
+    status_t getDefaultInterlaced(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+    status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+                                  uint32_t layerCount, uint64_t usage,
+                                  ui::Interlaced* outInterlaced);
+    status_t getDefaultChromaSiting(
+            uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+            uint64_t usage,
+            aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+    status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    ui::ChromaSiting* outChromaSiting);
+    status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+                                    uint32_t layerCount, uint64_t usage,
+                                    std::vector<ui::PlaneLayout>* outPlaneLayouts);
+
     const GrallocMapper& getGrallocMapper() const {
         return reinterpret_cast<const GrallocMapper&>(*mMapper);
     }
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index 5dc56c8..4bdacb0 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,28 +16,51 @@
 
 #pragma once
 
-#include <cinttypes>
-#include <cstdint>
-
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
 #include <android/hardware/graphics/common/1.1/types.h>
 #include <android/hardware/graphics/common/1.2/types.h>
 #include <system/graphics.h>
 
-#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
-
 namespace android {
 
-using PhysicalDisplayId = uint64_t;
-
-// android::ui::* in this header file will alias different types as
-// the HIDL interface is updated.
+/**
+ * android::ui::* in this header file will alias different types as
+ * the HIDL and stable AIDL interfaces are updated.
+ */
 namespace ui {
 
+/**
+ * The following HIDL types should be moved to their stable AIDL
+ * equivalents when composer moves to stable AIDL.
+ */
 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;
 
+/**
+ * Stable AIDL types
+ */
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::Smpte2086;
+
+/**
+ * The following stable AIDL types below have standard platform definitions
+ * that can be extended by vendors. The extensions are not defined here
+ * because they cannot be understood by the framework.
+ */
+using ChromaSiting = aidl::android::hardware::graphics::common::ChromaSiting;
+using Compression = aidl::android::hardware::graphics::common::Compression;
+using Interlaced = aidl::android::hardware::graphics::common::Interlaced;
+
 }  // namespace ui
 }  // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/libs/ui/include/ui/PhysicalDisplayId.h
similarity index 66%
copy from services/surfaceflinger/CompositionEngine/mock/Layer.cpp
copy to libs/ui/include/ui/PhysicalDisplayId.h
index 08483cb..1a345ac 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
+++ b/libs/ui/include/ui/PhysicalDisplayId.h
@@ -14,13 +14,19 @@
  * limitations under the License.
  */
 
-#include <compositionengine/mock/Layer.h>
+#pragma once
 
-namespace android::compositionengine::mock {
+#include <cinttypes>
+#include <cstdint>
 
-// The Google Mock documentation recommends explicit non-header instantiations
-// for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
+#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64
 
-} // namespace android::compositionengine::mock
+namespace android {
+
+using PhysicalDisplayId = uint64_t;
+
+constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) {
+    return static_cast<uint8_t>(displayId);
+}
+
+} // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 1768805..2f2229e 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_UI_RECT
 #define ANDROID_UI_RECT
 
+#include <ostream>
+
 #include <utils/Flattenable.h>
 #include <utils/Log.h>
 #include <utils/TypeHelpers.h>
@@ -214,6 +216,12 @@
     }
 };
 
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
+    *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
+        << ")";
+}
+
 ANDROID_BASIC_TYPES_TRAITS(Rect)
 
 }; // namespace android
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 79642ae..6bb7b8d 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -19,14 +19,15 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-
-#include <utils/Vector.h>
+#include <ostream>
 
 #include <ui/Rect.h>
 #include <utils/Flattenable.h>
 
 #include <android-base/macros.h>
 
+#include "FatVector.h"
+
 #include <string>
 
 namespace android {
@@ -119,6 +120,8 @@
     // returns true if the regions share the same underlying storage
     bool isTriviallyEqual(const Region& region) const;
 
+    // returns true if the regions consist of the same rectangle sequence
+    bool hasSameRects(const Region& region) const;
 
     /* various ways to access the rectangle list */
 
@@ -177,7 +180,7 @@
     // with an extra Rect as the last element which is set to the
     // bounds of the region. However, if the region is
     // a simple Rect then mStorage contains only that rect.
-    Vector<Rect> mStorage;
+    FatVector<Rect> mStorage;
 };
 
 
@@ -213,8 +216,22 @@
 Region& Region::operator += (const Point& pt) {
     return translateSelf(pt.x, pt.y);
 }
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Region& region, ::std::ostream* os) {
+    Region::const_iterator head = region.begin();
+    Region::const_iterator const tail = region.end();
+    bool first = true;
+    while (head != tail) {
+        *os << (first ? "Region(" : ", ");
+        PrintTo(*head, os);
+        head++;
+        first = false;
+    }
+    *os << ")";
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
 
 #endif // ANDROID_UI_REGION_H
-
diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h
new file mode 100644
index 0000000..89008f6
--- /dev/null
+++ b/libs/ui/include/ui/Rotation.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <type_traits>
+
+namespace android::ui {
+
+enum class Rotation { Rotation0 = 0, Rotation90 = 1, Rotation180 = 2, Rotation270 = 3 };
+
+// Equivalent to Surface.java constants.
+constexpr auto ROTATION_0 = Rotation::Rotation0;
+constexpr auto ROTATION_90 = Rotation::Rotation90;
+constexpr auto ROTATION_180 = Rotation::Rotation180;
+constexpr auto ROTATION_270 = Rotation::Rotation270;
+
+constexpr auto toRotation(std::underlying_type_t<Rotation> rotation) {
+    return static_cast<Rotation>(rotation);
+}
+
+constexpr auto toRotationInt(Rotation rotation) {
+    return static_cast<std::underlying_type_t<Rotation>>(rotation);
+}
+
+constexpr Rotation operator+(Rotation lhs, Rotation rhs) {
+    constexpr auto N = toRotationInt(ROTATION_270) + 1;
+    return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N);
+}
+
+constexpr const char* toCString(Rotation rotation) {
+    switch (rotation) {
+        case ROTATION_0:
+            return "ROTATION_0";
+        case ROTATION_90:
+            return "ROTATION_90";
+        case ROTATION_180:
+            return "ROTATION_180";
+        case ROTATION_270:
+            return "ROTATION_270";
+    }
+}
+
+} // namespace android::ui
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index f29a370..c6bb598 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_TRANSFORM_H
-#define ANDROID_TRANSFORM_H
+#pragma once
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <ostream>
 #include <string>
 
 #include <hardware/hardware.h>
@@ -27,6 +27,7 @@
 #include <math/vec3.h>
 #include <ui/Point.h>
 #include <ui/Rect.h>
+#include <ui/Rotation.h>
 
 namespace android {
 
@@ -38,16 +39,16 @@
 public:
     Transform();
     Transform(const Transform&  other);
-    explicit Transform(uint32_t orientation);
+    explicit Transform(uint32_t orientation, int w = 0, int h = 0);
     ~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,
+    enum RotationFlags : uint32_t {
+        ROT_0 = 0,
+        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
     };
 
@@ -63,6 +64,7 @@
     bool        preserveRects() const;
     uint32_t    getType() const;
     uint32_t    getOrientation() const;
+    bool operator==(const Transform& other) const;
 
     const vec3& operator [] (size_t i) const;  // returns column i
     float   tx() const;
@@ -98,6 +100,8 @@
     void dump(std::string& result, const char* name) const;
     void dump(const char* name) const;
 
+    static RotationFlags toRotationFlags(Rotation);
+
 private:
     struct mat33 {
         vec3 v[3];
@@ -115,7 +119,26 @@
     mutable uint32_t    mType;
 };
 
+inline void PrintTo(const Transform& t, ::std::ostream* os) {
+    std::string out;
+    t.dump(out, "ui::Transform");
+    *os << out;
+}
+
+inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) {
+    switch (rotation) {
+        case ROTATION_0:
+            return ROT_0;
+        case ROTATION_90:
+            return ROT_90;
+        case ROTATION_180:
+            return ROT_180;
+        case ROTATION_270:
+            return ROT_270;
+        default:
+            return ROT_INVALID;
+    }
+}
+
 }  // namespace ui
 }  // namespace android
-
-#endif /* ANDROID_TRANSFORM_H */
diff --git a/include/private/ui/RegionHelper.h b/libs/ui/include_private/ui/RegionHelper.h
similarity index 70%
rename from include/private/ui/RegionHelper.h
rename to libs/ui/include_private/ui/RegionHelper.h
index 0ec3e94..92cfba8 100644
--- a/include/private/ui/RegionHelper.h
+++ b/libs/ui/include_private/ui/RegionHelper.h
@@ -17,18 +17,17 @@
 #ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
 #define ANDROID_UI_PRIVATE_REGION_HELPER_H
 
-#include <limits>
 #include <stdint.h>
 #include <sys/types.h>
+#include <limits>
 
 #include <limits>
 
 namespace android {
 // ----------------------------------------------------------------------------
 
-template<typename RECT>
-class region_operator
-{
+template <typename RECT>
+class region_operator {
 public:
     typedef typename RECT::value_type TYPE;
     static const TYPE max_value = std::numeric_limits<TYPE>::max();
@@ -40,39 +39,32 @@
      *    their corresponding value with the above formulae and use
      *    it when instantiating a region_operator.
      */
-    static const uint32_t LHS = 0x5;  // 0b101
-    static const uint32_t RHS = 0x6;  // 0b110
-    enum {
-        op_nand = LHS & ~RHS,
-        op_and  = LHS &  RHS,
-        op_or   = LHS |  RHS,
-        op_xor  = LHS ^  RHS
-    };
+    static const uint32_t LHS = 0x5; // 0b101
+    static const uint32_t RHS = 0x6; // 0b110
+    enum { op_nand = LHS & ~RHS, op_and = LHS & RHS, op_or = LHS | RHS, op_xor = LHS ^ RHS };
 
     struct region {
         RECT const* rects;
         size_t count;
         TYPE dx;
         TYPE dy;
-        inline region(const region& rhs) 
-            : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { }
-        inline region(RECT const* _r, size_t _c)
-            : rects(_r), count(_c), dx(), dy() { }
+        inline region(const region& rhs)
+              : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) {}
+        inline region(RECT const* _r, size_t _c) : rects(_r), count(_c), dx(), dy() {}
         inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy)
-            : rects(_r), count(_c), dx(_dx), dy(_dy) { }
+              : rects(_r), count(_c), dx(_dx), dy(_dy) {}
     };
 
     class region_rasterizer {
         friend class region_operator;
         virtual void operator()(const RECT& rect) = 0;
+
     public:
-        virtual ~region_rasterizer() { }
+        virtual ~region_rasterizer() {}
     };
-    
+
     inline region_operator(uint32_t op, const region& lhs, const region& rhs)
-        : op_mask(op), spanner(lhs, rhs) 
-    {
-    }
+          : op_mask(op), spanner(lhs, rhs) {}
 
     void operator()(region_rasterizer& rasterizer) {
         RECT current(Rect::EMPTY_RECT);
@@ -83,31 +75,26 @@
             do {
                 int inner_inside = spannerInner.next(current.left, current.right);
                 if ((op_mask >> inner_inside) & 1) {
-                    if (current.left < current.right && 
-                            current.top < current.bottom) {
+                    if (current.left < current.right && current.top < current.bottom) {
                         rasterizer(current);
                     }
                 }
-            } while(!spannerInner.isDone());
-        } while(!spanner.isDone());
+            } while (!spannerInner.isDone());
+        } while (!spanner.isDone());
     }
 
-private:    
+private:
     uint32_t op_mask;
 
-    class SpannerBase
-    {
+    class SpannerBase {
     public:
         SpannerBase()
-            : lhs_head(max_value), lhs_tail(max_value),
-              rhs_head(max_value), rhs_tail(max_value) {
-        }
+              : lhs_head(max_value),
+                lhs_tail(max_value),
+                rhs_head(max_value),
+                rhs_tail(max_value) {}
 
-        enum {
-            lhs_before_rhs   = 0,
-            lhs_after_rhs    = 1,
-            lhs_coincide_rhs = 2
-        };
+        enum { lhs_before_rhs = 0, lhs_after_rhs = 1, lhs_coincide_rhs = 2 };
 
     protected:
         TYPE lhs_head;
@@ -115,9 +102,7 @@
         TYPE rhs_head;
         TYPE rhs_tail;
 
-        inline int next(TYPE& head, TYPE& tail,
-                bool& more_lhs, bool& more_rhs) 
-        {
+        inline int next(TYPE& head, TYPE& tail, bool& more_lhs, bool& more_rhs) {
             int inside;
             more_lhs = false;
             more_rhs = false;
@@ -157,32 +142,26 @@
         }
     };
 
-    class Spanner : protected SpannerBase 
-    {
+    class Spanner : protected SpannerBase {
         friend class region_operator;
         region lhs;
         region rhs;
 
     public:
-        inline Spanner(const region& _lhs, const region& _rhs)
-        : lhs(_lhs), rhs(_rhs)
-        {
+        inline Spanner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {
             if (lhs.count) {
-                SpannerBase::lhs_head = lhs.rects->top      + lhs.dy;
-                SpannerBase::lhs_tail = lhs.rects->bottom   + lhs.dy;
+                SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
+                SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
             }
             if (rhs.count) {
-                SpannerBase::rhs_head = rhs.rects->top      + rhs.dy;
-                SpannerBase::rhs_tail = rhs.rects->bottom   + rhs.dy;
+                SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
+                SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
             }
         }
 
-        inline bool isDone() const {
-            return !rhs.count && !lhs.count;
-        }
+        inline bool isDone() const { return !rhs.count && !lhs.count; }
 
-        inline int next(TYPE& top, TYPE& bottom) 
-        {
+        inline int next(TYPE& top, TYPE& bottom) {
             bool more_lhs = false;
             bool more_rhs = false;
             int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs);
@@ -196,22 +175,21 @@
         }
 
     private:
-        static inline 
-        void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
+        static inline void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
             // got to next span
             size_t count = reg.count;
-            RECT const * rects = reg.rects;
-            RECT const * const end = rects + count;
+            RECT const* rects = reg.rects;
+            RECT const* const end = rects + count;
             const int top = rects->top;
             while (rects != end && rects->top == top) {
                 rects++;
                 count--;
             }
             if (rects != end) {
-                aTop    = rects->top    + reg.dy;
+                aTop = rects->top + reg.dy;
                 aBottom = rects->bottom + reg.dy;
             } else {
-                aTop    = max_value;
+                aTop = max_value;
                 aBottom = max_value;
             }
             reg.rects = rects;
@@ -219,21 +197,17 @@
         }
     };
 
-    class SpannerInner : protected SpannerBase 
-    {
+    class SpannerInner : protected SpannerBase {
         region lhs;
         region rhs;
-        
+
     public:
-        inline SpannerInner(const region& _lhs, const region& _rhs)
-            : lhs(_lhs), rhs(_rhs)
-        {
-        }
+        inline SpannerInner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {}
 
         inline void prepare(int inside) {
             if (inside == SpannerBase::lhs_before_rhs) {
                 if (lhs.count) {
-                    SpannerBase::lhs_head = lhs.rects->left  + lhs.dx;
+                    SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
                     SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
                 }
                 SpannerBase::rhs_head = max_value;
@@ -242,28 +216,26 @@
                 SpannerBase::lhs_head = max_value;
                 SpannerBase::lhs_tail = max_value;
                 if (rhs.count) {
-                    SpannerBase::rhs_head = rhs.rects->left  + rhs.dx;
+                    SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
                     SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
                 }
             } else {
                 if (lhs.count) {
-                    SpannerBase::lhs_head = lhs.rects->left  + lhs.dx;
+                    SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
                     SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
                 }
                 if (rhs.count) {
-                    SpannerBase::rhs_head = rhs.rects->left  + rhs.dx;
+                    SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
                     SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
                 }
             }
         }
 
         inline bool isDone() const {
-            return SpannerBase::lhs_head == max_value && 
-                   SpannerBase::rhs_head == max_value;
+            return SpannerBase::lhs_head == max_value && SpannerBase::rhs_head == max_value;
         }
 
-        inline int next(TYPE& left, TYPE& right) 
-        {
+        inline int next(TYPE& left, TYPE& right) {
             bool more_lhs = false;
             bool more_rhs = false;
             int inside = SpannerBase::next(left, right, more_lhs, more_rhs);
@@ -277,17 +249,16 @@
         }
 
     private:
-        static inline 
-        void advance(region& reg, TYPE& left, TYPE& right) {
+        static inline void advance(region& reg, TYPE& left, TYPE& right) {
             if (reg.rects && reg.count) {
                 const int cur_span_top = reg.rects->top;
                 reg.rects++;
                 reg.count--;
                 if (!reg.count || reg.rects->top != cur_span_top) {
-                    left  = max_value;
+                    left = max_value;
                     right = max_value;
                 } else {
-                    left  = reg.rects->left  + reg.dx;
+                    left = reg.rects->left + reg.dx;
                     right = reg.rects->right + reg.dx;
                 }
             }
@@ -298,6 +269,6 @@
 };
 
 // ----------------------------------------------------------------------------
-};
+}; // namespace android
 
 #endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */
diff --git a/libs/ui/include_vndk/ui/DisplayConfig.h b/libs/ui/include_vndk/ui/DisplayConfig.h
new file mode 120000
index 0000000..1450319
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayConfig.h
@@ -0,0 +1 @@
+../../include/ui/DisplayConfig.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/DisplayState.h b/libs/ui/include_vndk/ui/DisplayState.h
new file mode 120000
index 0000000..4e92849
--- /dev/null
+++ b/libs/ui/include_vndk/ui/DisplayState.h
@@ -0,0 +1 @@
+../../include/ui/DisplayState.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h
new file mode 120000
index 0000000..bf30166
--- /dev/null
+++ b/libs/ui/include_vndk/ui/FatVector.h
@@ -0,0 +1 @@
+../../include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Gralloc2.h b/libs/ui/include_vndk/ui/Gralloc2.h
deleted file mode 120000
index 66098c4..0000000
--- a/libs/ui/include_vndk/ui/Gralloc2.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/Gralloc2.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
new file mode 120000
index 0000000..6e3fb1e
--- /dev/null
+++ b/libs/ui/include_vndk/ui/PhysicalDisplayId.h
@@ -0,0 +1 @@
+../../include/ui/PhysicalDisplayId.h
\ No newline at end of file
diff --git a/libs/ui/include_vndk/ui/Rotation.h b/libs/ui/include_vndk/ui/Rotation.h
new file mode 120000
index 0000000..d84fb4b
--- /dev/null
+++ b/libs/ui/include_vndk/ui/Rotation.h
@@ -0,0 +1 @@
+../../include/ui/Rotation.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 0a07a48..ff55bc2 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -38,8 +38,9 @@
         "libgmock",
     ],
     shared_libs: [
+        "libhidlbase",
         "liblog",
-	"libui",
+        "libui",
     ],
     srcs: [
         "GraphicBufferAllocator_test.cpp",
@@ -65,6 +66,24 @@
     cflags: ["-Wall", "-Werror"],
 }
 
+// This test has a main method, and requires a separate binary to be built.
+cc_test {
+    name: "GraphicBufferOverBinder_test",
+    srcs: ["GraphicBufferOverBinder_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+    header_libs: [
+        "libdvr_headers",
+    ],
+    shared_libs: [
+        "android.frameworks.bufferhub@1.0",
+        "libbinder",
+        "libgui",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+}
+
 cc_test {
     name: "BufferHub_test",
     header_libs: [
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index efca083..f4c0afa 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
         std::cout << "Setting expected stride to " << stride << std::endl;
         EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
                     allocate)
-                .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
+                .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
     }
     std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
 };
diff --git a/libs/ui/tests/GraphicBufferOverBinder_test.cpp b/libs/ui/tests/GraphicBufferOverBinder_test.cpp
new file mode 100644
index 0000000..7c0a44a
--- /dev/null
+++ b/libs/ui/tests/GraphicBufferOverBinder_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 "GraphicBufferOverBinder_test"
+
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <gui/BufferQueue.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <ui/BufferHubBuffer.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Log.h>
+
+namespace android {
+
+constexpr uint32_t kTestWidth = 1024;
+constexpr uint32_t kTestHeight = 1;
+constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr uint32_t kTestLayerCount = 1;
+constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+static const String16 kTestServiceName = String16("GraphicBufferOverBinderTestService");
+enum GraphicBufferOverBinderTestServiceCode {
+    GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+    GRAPHIC_BUFFER_FROM_BUFFER_HUB_BUFFER,
+};
+
+class GraphicBufferOverBinderTestService : public BBinder {
+public:
+    GraphicBufferOverBinderTestService() {
+        // GraphicBuffer
+        mGraphicBuffer = new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount,
+                                           kTestUsage);
+        ALOGI("mGraphicBuffer id %" PRIi32, mGraphicBuffer->getBufferId());
+
+        // BufferHub-backed GraphicBuffer
+        std::unique_ptr<BufferHubBuffer> bufferHubBuffer =
+                BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat,
+                                        kTestUsage, /*userMetadataSize=*/0);
+        mBufferhubBackedGraphicBuffer = new GraphicBuffer(std::move(bufferHubBuffer));
+        if (!mBufferhubBackedGraphicBuffer->isBufferHubBuffer()) {
+            ALOGE("Failed to back GraphicBuffer with BufferHub.");
+        }
+        if (bufferHubBuffer != nullptr) {
+            ALOGE("Failed to move BufferHubBuffer to GraphicBuffer");
+        }
+        ALOGI("mBufferhubBackedGraphicBuffer id %" PRIi32,
+              mBufferhubBackedGraphicBuffer->getBufferId());
+    }
+
+    ~GraphicBufferOverBinderTestService() = default;
+
+    virtual status_t onTransact(uint32_t code, const Parcel& /*data*/, Parcel* reply,
+                                uint32_t /*flags*/ = 0) {
+        switch (code) {
+            case GRAPHIC_BUFFER: {
+                return reply->write(*mGraphicBuffer);
+            }
+            case GRAPHIC_BUFFER_FROM_BUFFER_HUB_BUFFER: {
+                return reply->write(*mBufferhubBackedGraphicBuffer);
+            }
+            default:
+                return UNKNOWN_TRANSACTION;
+        };
+    }
+
+protected:
+    sp<GraphicBuffer> mGraphicBuffer;
+    sp<GraphicBuffer> mBufferhubBackedGraphicBuffer;
+};
+
+static int runBinderServer() {
+    ProcessState::self()->startThreadPool();
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<GraphicBufferOverBinderTestService> service = new GraphicBufferOverBinderTestService;
+    sm->addService(kTestServiceName, service, false);
+
+    ALOGI("Binder server running...");
+
+    while (true) {
+        int stat, retval;
+        retval = wait(&stat);
+        if (retval == -1 && errno == ECHILD) {
+            break;
+        }
+    }
+
+    ALOGI("Binder server exiting...");
+    return 0;
+}
+
+class GraphicBufferOverBinderTest : public ::testing::TestWithParam<uint32_t> {
+protected:
+    virtual void SetUp() {
+        mService = defaultServiceManager()->getService(kTestServiceName);
+        if (mService == nullptr) {
+            ALOGE("Failed to connect to the test service.");
+            return;
+        }
+
+        ALOGI("Binder service is ready for client.");
+    }
+
+    status_t GetGraphicBuffer(sp<GraphicBuffer>* outBuf, uint32_t opCode) {
+        Parcel data;
+        Parcel reply;
+        status_t error = mService->transact(opCode, data, &reply);
+        if (error != NO_ERROR) {
+            ALOGE("Failed to get graphic buffer over binder, error=%d.", error);
+            return error;
+        }
+
+        *outBuf = new GraphicBuffer();
+        return reply.read(**outBuf);
+    }
+
+private:
+    sp<IBinder> mService;
+};
+
+TEST_F(GraphicBufferOverBinderTest, SendGraphicBufferOverBinder) {
+    sp<GraphicBuffer> gb;
+    EXPECT_EQ(GetGraphicBuffer(&gb, GRAPHIC_BUFFER), OK);
+    EXPECT_NE(gb, nullptr);
+    EXPECT_FALSE(gb->isBufferHubBuffer());
+    void* vaddr;
+    EXPECT_EQ(gb->lock(kTestUsage, &vaddr), OK);
+    EXPECT_EQ(gb->unlock(), OK);
+}
+
+TEST_F(GraphicBufferOverBinderTest, SendGraphicBufferFromBufferHubBufferOverBinder) {
+    sp<GraphicBuffer> gb;
+    EXPECT_EQ(GetGraphicBuffer(&gb, GRAPHIC_BUFFER_FROM_BUFFER_HUB_BUFFER), NO_ERROR);
+    EXPECT_NE(gb, nullptr);
+    EXPECT_TRUE(gb->isBufferHubBuffer());
+    void* vaddr;
+    EXPECT_EQ(gb->lock(kTestUsage, &vaddr), OK);
+    EXPECT_EQ(gb->unlock(), OK);
+}
+
+} // namespace android
+
+int main(int argc, char** argv) {
+    pid_t pid = fork();
+    if (pid == 0) {
+        android::ProcessState::self()->startThreadPool();
+        ::testing::InitGoogleTest(&argc, argv);
+        return RUN_ALL_TESTS();
+
+    } else {
+        ALOGI("Test process pid: %d.", pid);
+        return android::runBinderServer();
+    }
+}
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
index 127f7ee..5e0b094 100644
--- a/libs/ui/tests/GraphicBuffer_test.cpp
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -44,11 +44,28 @@
 
 TEST_F(GraphicBufferTest, AllocateBadDimensions) {
     PixelFormat format = PIXEL_FORMAT_RGBA_8888;
+    if (std::numeric_limits<size_t>::max() / std::numeric_limits<uint32_t>::max() /
+                bytesPerPixel(format) >=
+        std::numeric_limits<uint32_t>::max()) {
+        GTEST_SUCCEED() << "Cannot overflow with this format";
+    }
     uint32_t width, height;
     width = height = std::numeric_limits<uint32_t>::max();
     sp<GraphicBuffer> gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage,
                                            std::string("test")));
     ASSERT_EQ(BAD_VALUE, gb->initCheck());
+
+    const size_t targetArea = std::numeric_limits<size_t>::max() / bytesPerPixel(format);
+    const size_t widthCandidate = targetArea / std::numeric_limits<uint32_t>::max();
+    if (widthCandidate == 0) {
+        width = 1;
+    } else {
+        width = std::numeric_limits<uint32_t>::max();
+    }
+    height = (targetArea / width) + 1;
+    sp<GraphicBuffer> gb2(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage,
+                                            std::string("test")));
+    ASSERT_EQ(BAD_VALUE, gb2->initCheck());
 }
 
 TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) {
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 22c80a4..d62e3e2 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -32,11 +32,11 @@
     ~MockGrallocAllocator() override;
 
     MOCK_METHOD(bool, isLoaded, (), (const, override));
-    MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override));
+    MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
     MOCK_METHOD(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),
+                (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
+                 uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+                 buffer_handle_t* outBufferHandles, bool less),
                 (const, override));
 };
 
diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp
index 115e866..7823e36 100644
--- a/libs/vr/libbufferhub/consumer_buffer.cpp
+++ b/libs/vr/libbufferhub/consumer_buffer.cpp
@@ -52,12 +52,6 @@
   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(
@@ -152,12 +146,6 @@
   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());
   }
diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp
index 3d88ba5..aa9d072 100644
--- a/libs/vr/libbufferhub/producer_buffer.cpp
+++ b/libs/vr/libbufferhub/producer_buffer.cpp
@@ -96,13 +96,6 @@
   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(
@@ -186,15 +179,6 @@
   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::isAnyClientAcquired(current_buffer_state) ||
         BufferHubDefs::isAnyClientGained(current_buffer_state) ||
         (BufferHubDefs::isAnyClientPosted(
diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp
index f67e258..62856df 100644
--- a/libs/vr/libdisplay/display_client.cpp
+++ b/libs/vr/libdisplay/display_client.cpp
@@ -178,6 +178,10 @@
   return status;
 }
 
+Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() {
+  return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>();
+}
+
 Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface(
     const SurfaceAttributes& attributes) {
   int error;
diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h
index f8f5b3d..81546ac 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_client.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_client.h
@@ -72,6 +72,7 @@
  public:
   pdx::Status<Metrics> GetDisplayMetrics();
   pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type);
+  pdx::Status<uint8_t> GetDisplayIdentificationPort();
   pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer(
       DvrGlobalBufferKey key, size_t size, uint64_t usage);
   pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key);
diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
index 3786d1d..9f4cc4a 100644
--- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h
+++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h
@@ -191,7 +191,8 @@
 enum class ConfigFileType : uint32_t {
   kLensMetrics,
   kDeviceMetrics,
-  kDeviceConfiguration
+  kDeviceConfiguration,
+  kDeviceEdid
 };
 
 struct DisplayProtocol {
@@ -210,6 +211,7 @@
     kOpGetSurfaceInfo,
     kOpCreateQueue,
     kOpSetAttributes,
+    kOpGetDisplayIdentificationPort,
   };
 
   // Aliases.
@@ -220,6 +222,8 @@
   PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void));
   PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData,
                     std::string(ConfigFileType config_type));
+  PDX_REMOTE_METHOD(GetDisplayIdentificationPort,
+                    kOpGetDisplayIdentificationPort, uint8_t(Void));
   PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer,
                     LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size,
                                             uint64_t usage));
diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h
index e383bb2..b7abb99 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api.h
@@ -85,6 +85,8 @@
   DVR_CONFIGURATION_DATA_DEVICE_METRICS = 1,
   // Request the per device configuration data file.
   DVR_CONFIGURATION_DATA_DEVICE_CONFIG = 2,
+  // Request the edid data for the display.
+  DVR_CONFIGURATION_DATA_DEVICE_EDID = 3,
 };
 
 // dvr_display_manager.h
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 12ce74b..abc64bd 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -35,11 +35,12 @@
 ]
 
 sharedLibraries = [
-    "android.frameworks.vr.composer@1.0",
+    "android.frameworks.vr.composer@2.0",
     "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.graphics.composer@2.4",
     "libbinder",
     "libbase",
     "libbufferhubqueue",
@@ -65,6 +66,7 @@
     "android.hardware.graphics.composer@2.1-command-buffer",
     "android.hardware.graphics.composer@2.2-command-buffer",
     "android.hardware.graphics.composer@2.3-command-buffer",
+    "android.hardware.graphics.composer@2.4-command-buffer",
     "libdvr_headers",
     "libsurfaceflinger_headers",
 ]
diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp
index 87162c0..582fed3 100644
--- a/libs/vr/libvrflinger/display_service.cpp
+++ b/libs/vr/libvrflinger/display_service.cpp
@@ -18,6 +18,8 @@
 #include <private/dvr/trusted_uids.h>
 #include <private/dvr/types.h>
 
+#include "DisplayHardware/DisplayIdentification.h"
+
 using android::dvr::display::DisplayProtocol;
 using android::pdx::Channel;
 using android::pdx::ErrorStatus;
@@ -139,6 +141,11 @@
           *this, &DisplayService::OnGetConfigurationData, message);
       return {};
 
+    case DisplayProtocol::GetDisplayIdentificationPort::Opcode:
+      DispatchRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(
+          *this, &DisplayService::OnGetDisplayIdentificationPort, message);
+      return {};
+
     case DisplayProtocol::CreateSurface::Opcode:
       DispatchRemoteMethod<DisplayProtocol::CreateSurface>(
           *this, &DisplayService::OnCreateSurface, message);
@@ -194,6 +201,7 @@
 pdx::Status<std::string> DisplayService::OnGetConfigurationData(
     pdx::Message& /*message*/, display::ConfigFileType config_type) {
   std::string property_name;
+  DisplayIdentificationData display_identification_data;
   switch (config_type) {
     case display::ConfigFileType::kLensMetrics:
       property_name = kDvrLensMetricsProperty;
@@ -204,6 +212,14 @@
     case display::ConfigFileType::kDeviceConfiguration:
       property_name = kDvrDeviceConfigProperty;
       break;
+    case display::ConfigFileType::kDeviceEdid:
+      display_identification_data =
+          hardware_composer_.GetCurrentDisplayIdentificationData();
+      if (display_identification_data.size() == 0) {
+        return ErrorStatus(ENOENT);
+      }
+      return std::string(display_identification_data.begin(),
+                         display_identification_data.end());
     default:
       return ErrorStatus(EINVAL);
   }
@@ -220,6 +236,11 @@
   return std::move(data);
 }
 
+pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort(
+    pdx::Message& /*message*/) {
+  return hardware_composer_.GetCurrentDisplayPort();
+}
+
 // Creates a new DisplaySurface and associates it with this channel. This may
 // only be done once per channel.
 Status<display::SurfaceInfo> DisplayService::OnCreateSurface(
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index e0f2edd..89f1eae 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -80,6 +80,7 @@
   pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message);
   pdx::Status<std::string> OnGetConfigurationData(
       pdx::Message& message, display::ConfigFileType config_type);
+  pdx::Status<uint8_t> OnGetDisplayIdentificationPort(pdx::Message& message);
   pdx::Status<display::SurfaceInfo> OnCreateSurface(
       pdx::Message& message, const display::SurfaceAttributes& attributes);
   pdx::Status<BorrowedNativeBufferHandle> OnSetupGlobalBuffer(
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index e1240d6..b771538 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -51,8 +51,6 @@
 
 const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns";
 
-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";
@@ -139,6 +137,20 @@
   composer_callback_->SetVsyncService(nullptr);
 }
 
+void HardwareComposer::UpdateEdidData(Hwc2::Composer* composer,
+                                      hwc2_display_t hw_id) {
+  const auto error = composer->getDisplayIdentificationData(
+      hw_id, &display_port_, &display_identification_data_);
+  if (error != android::hardware::graphics::composer::V2_1::Error::NONE) {
+    if (error !=
+        android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) {
+      ALOGI("hardware_composer: identification data error\n");
+    } else {
+      ALOGI("hardware_composer: identification data unsupported\n");
+    }
+  }
+}
+
 bool HardwareComposer::Initialize(
     Hwc2::Composer* composer, hwc2_display_t primary_display_id,
     RequestDisplayCallback request_display_callback) {
@@ -166,6 +178,8 @@
       "HardwareComposer: Failed to create interrupt event fd : %s",
       strerror(errno));
 
+  UpdateEdidData(composer, primary_display_id);
+
   post_thread_ = std::thread(&HardwareComposer::PostThread, this);
 
   initialized_ = true;
@@ -965,18 +979,9 @@
       external_display_ = GetDisplayParams(composer_.get(),
           *displays.external_display, /*is_primary*/ false);
 
-      if (property_get_bool(kUseExternalDisplayProperty, false)) {
-        ALOGI("External display connected. Switching to external display.");
-        target_display_ = &(*external_display_);
-        target_display_changed = true;
-      } else {
-        ALOGI("External display connected, but sysprop %s is unset, so"
-              " using primary display.", kUseExternalDisplayProperty);
-        if (was_using_external_display) {
-          target_display_ = &primary_display_;
-          target_display_changed = true;
-        }
-      }
+      ALOGI("External display connected. Switching to external display.");
+      target_display_ = &(*external_display_);
+      target_display_changed = true;
     } else {
       // External display was disconnected
       external_display_ = std::nullopt;
@@ -999,6 +1004,9 @@
       EnableDisplay(*external_display_, false);
     }
 
+    // Update the cached edid data for the current display.
+    UpdateEdidData(composer_.get(), target_display_->id);
+
     // Turn the new target display on.
     EnableDisplay(*target_display_, true);
 
@@ -1193,6 +1201,26 @@
   return Void();
 }
 
+Return<void> HardwareComposer::ComposerCallback::onVsync_2_4(
+    Hwc2::Display /*display*/, int64_t /*timestamp*/,
+    Hwc2::VsyncPeriodNanos /*vsyncPeriodNanos*/) {
+  LOG_ALWAYS_FATAL("Unexpected onVsync_2_4 callback");
+  return Void();
+}
+
+Return<void> HardwareComposer::ComposerCallback::onVsyncPeriodTimingChanged(
+    Hwc2::Display /*display*/,
+    const Hwc2::VsyncPeriodChangeTimeline& /*updatedTimeline*/) {
+  LOG_ALWAYS_FATAL("Unexpected onVsyncPeriodTimingChanged callback");
+  return Void();
+}
+
+Return<void> HardwareComposer::ComposerCallback::onSeamlessPossible(
+    Hwc2::Display /*display*/) {
+  LOG_ALWAYS_FATAL("Unexpected onSeamlessPossible callback");
+  return Void();
+}
+
 void HardwareComposer::ComposerCallback::SetVsyncService(
     const sp<VsyncService>& vsync_service) {
   std::lock_guard<std::mutex> lock(mutex_);
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index db0d6a7..bfce10b 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -25,6 +25,7 @@
 #include <private/dvr/shared_buffer_helpers.h>
 #include <private/dvr/vsync_service.h>
 
+#include "DisplayHardware/DisplayIdentification.h"
 #include "acquired_buffer.h"
 #include "display_surface.h"
 
@@ -334,6 +335,14 @@
   int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer);
   void OnDeletedGlobalBuffer(DvrGlobalBufferKey key);
 
+  // Gets the edid data for the current active display (internal or external)
+  DisplayIdentificationData GetCurrentDisplayIdentificationData() {
+    return display_identification_data_;
+  }
+
+  // Gets the edid port for the current active display (internal or external)
+  uint8_t GetCurrentDisplayPort() { return display_port_; }
+
  private:
   DisplayParams GetDisplayParams(Hwc2::Composer* composer,
       hwc2_display_t display, bool is_primary);
@@ -366,6 +375,13 @@
     hardware::Return<void> onRefresh(Hwc2::Display display) override;
     hardware::Return<void> onVsync(Hwc2::Display display,
                                    int64_t timestamp) override;
+    hardware::Return<void> onVsync_2_4(
+        Hwc2::Display display, int64_t timestamp,
+        Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override;
+    hardware::Return<void> onVsyncPeriodTimingChanged(
+        Hwc2::Display display,
+        const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
+    hardware::Return<void> onSeamlessPossible(Hwc2::Display display) override;
 
     bool GotFirstHotplug() { return got_first_hotplug_; }
     void SetVsyncService(const sp<VsyncService>& vsync_service);
@@ -544,6 +560,11 @@
   bool vsync_trace_parity_ = false;
   sp<VsyncService> vsync_service_;
 
+  // Edid section.
+  void UpdateEdidData(Hwc2::Composer* composer, hwc2_display_t hw_id);
+  DisplayIdentificationData display_identification_data_;
+  uint8_t display_port_;
+
   static constexpr int kPostThreadInterrupted = 1;
 
   HardwareComposer(const HardwareComposer&) = delete;
diff --git a/opengl/libagl/Android.bp b/opengl/libagl/Android.bp
deleted file mode 100644
index f5bf015..0000000
--- a/opengl/libagl/Android.bp
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// Build the software OpenGL ES library
-//
-
-cc_defaults {
-    name: "libGLES_android_defaults",
-
-    cflags: [
-        "-DLOG_TAG=\"libagl\"",
-        "-DGL_GLEXT_PROTOTYPES",
-        "-DEGL_EGLEXT_PROTOTYPES",
-        "-fvisibility=hidden",
-        "-Wall",
-        "-Werror",
-    ],
-
-    shared_libs: [
-        "libcutils",
-        "libhardware",
-        "libutils",
-        "liblog",
-        "libpixelflinger",
-        "libETC1",
-        "libui",
-        "libnativewindow",
-    ],
-
-    header_libs: [
-        "bionic_libc_platform_headers",
-    ],
-
-    arch: {
-        arm: {
-            cflags: ["-fstrict-aliasing"],
-        },
-
-        mips: {
-            cflags: [
-                "-fstrict-aliasing",
-                // The graphics code can generate division by zero
-                "-mno-check-zero-division",
-            ],
-        },
-    },
-}
-
-cc_library_shared {
-    name: "libGLES_android",
-    defaults: ["libGLES_android_defaults"],
-
-    whole_static_libs: ["libGLES_android_arm"],
-
-    srcs: [
-        "egl.cpp",
-        "state.cpp",
-        "texture.cpp",
-        "Tokenizer.cpp",
-        "TokenManager.cpp",
-        "TextureObjectManager.cpp",
-        "BufferObjectManager.cpp",
-    ],
-
-    arch: {
-        arm: {
-            srcs: [
-                "fixed_asm.S",
-                "iterators.S",
-            ],
-        },
-
-        mips: {
-            rev6: {
-                srcs: ["arch-mips/fixed_asm.S"],
-            },
-        },
-    },
-
-    relative_install_path: "egl",
-}
-
-cc_library_static {
-    name: "libGLES_android_arm",
-    defaults: ["libGLES_android_defaults"],
-
-    srcs: [
-        "array.cpp",
-        "fp.cpp",
-        "light.cpp",
-        "matrix.cpp",
-        "mipmap.cpp",
-        "primitives.cpp",
-        "vertex.cpp",
-    ],
-
-    arch: {
-        arm: {
-            instruction_set: "arm",
-        },
-    },
-}
diff --git a/opengl/libagl/BufferObjectManager.cpp b/opengl/libagl/BufferObjectManager.cpp
deleted file mode 100644
index 3d93c19..0000000
--- a/opengl/libagl/BufferObjectManager.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- ** Copyright 2008, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License"); 
- ** you may not use this file except in compliance with the License. 
- ** You may obtain a copy of the License at 
- **
- **     http://www.apache.org/licenses/LICENSE-2.0 
- **
- ** Unless required by applicable law or agreed to in writing, software 
- ** distributed under the License is distributed on an "AS IS" BASIS, 
- ** WITHOUT 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 <stddef.h>
-#include <sys/types.h>
-
-#include <cutils/atomic.h>
-#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
-#include <utils/Errors.h>
-
-#include <GLES/gl.h>
-
-#include "BufferObjectManager.h"
-
-
-namespace android {
-
-using namespace gl;
-
-// ----------------------------------------------------------------------------
-
-EGLBufferObjectManager::EGLBufferObjectManager() 
-: TokenManager(), mCount(0)
-{
-}
-
-EGLBufferObjectManager::~EGLBufferObjectManager()
-{
-    // destroy all the buffer objects and their storage
-    GLsizei n = mBuffers.size();
-    for (GLsizei i=0 ; i<n ; i++) {
-        buffer_t* bo = mBuffers.valueAt(i);
-        free(bo->data);
-        delete bo;
-    }
-}
-
-buffer_t const* EGLBufferObjectManager::bind(GLuint buffer)
-{
-    Mutex::Autolock _l(mLock);
-    int32_t i = mBuffers.indexOfKey(buffer);
-    if (i >= 0) {
-        return mBuffers.valueAt(i);
-    }
-    buffer_t* bo = new buffer_t;
-    bo->data = 0;
-    bo->usage = GL_STATIC_DRAW;
-    bo->size = 0;
-    bo->name = buffer;
-    mBuffers.add(buffer, bo);
-    return bo;
-}
-
-int EGLBufferObjectManager::allocateStore(buffer_t* bo,
-        GLsizeiptr size, GLenum usage)
-{
-    Mutex::Autolock _l(mLock);
-    if (size != bo->size) {
-       uint8_t* data = (uint8_t*)malloc(size);
-        if (data == 0)
-            return -1;
-        free(bo->data);
-        bo->data = data;
-        bo->size = size;
-    }
-    bo->usage = usage;
-    return 0;
-}
-
-void EGLBufferObjectManager::deleteBuffers(GLsizei n, const GLuint* buffers)
-{
-    Mutex::Autolock _l(mLock);
-    while (n--) {
-        const GLuint t = *buffers++;
-        if (t) {
-            int32_t index = mBuffers.indexOfKey(t);
-            if (index >= 0) {
-                buffer_t* bo = mBuffers.valueAt(index);
-                free(bo->data);
-                mBuffers.removeItemsAt(index);
-                delete bo;
-            }
-        }
-    }
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/opengl/libagl/BufferObjectManager.h b/opengl/libagl/BufferObjectManager.h
deleted file mode 100644
index fcdae5b..0000000
--- a/opengl/libagl/BufferObjectManager.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- **
- ** Copyright 2006, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License"); 
- ** you may not use this file except in compliance with the License. 
- ** You may obtain a copy of the License at 
- **
- **     http://www.apache.org/licenses/LICENSE-2.0 
- **
- ** Unless required by applicable law or agreed to in writing, software 
- ** distributed under the License is distributed on an "AS IS" BASIS, 
- ** WITHOUT 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_OPENGLES_BUFFER_OBJECT_MANAGER_H
-#define ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H
-
-#include <atomic>
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
-#include <utils/Errors.h>
-
-#include <GLES/gl.h>
-
-#include "Tokenizer.h"
-#include "TokenManager.h"
-
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-namespace gl {
-
-struct buffer_t {
-    GLsizeiptr      size;
-    GLenum          usage;
-    uint8_t*        data;
-    uint32_t        name;
-};
-
-};
-
-class EGLBufferObjectManager : public TokenManager
-{
-public:
-    EGLBufferObjectManager();
-    ~EGLBufferObjectManager();
-
-    // protocol for sp<>
-    inline  void    incStrong(const void* id) const;
-    inline  void    decStrong(const void* id) const;
-    typedef void    weakref_type;
-
-    gl::buffer_t const* bind(GLuint buffer);
-    int                 allocateStore(gl::buffer_t* bo, GLsizeiptr size, GLenum usage);
-    void                deleteBuffers(GLsizei n, const GLuint* buffers);
-
-private:
-    mutable std::atomic_size_t          mCount;
-    mutable Mutex                       mLock;
-    KeyedVector<GLuint, gl::buffer_t*>  mBuffers;
-};
-
-void EGLBufferObjectManager::incStrong(const void* /*id*/) const {
-    mCount.fetch_add(1, std::memory_order_relaxed);
-}
-void EGLBufferObjectManager::decStrong(const void* /*id*/) const {
-    if (mCount.fetch_sub(1, std::memory_order_release) == 0) {
-        std::atomic_thread_fence(std::memory_order_acquire);
-        delete this;
-    }
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H
-
diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp
deleted file mode 100644
index 06d45cc..0000000
--- a/opengl/libagl/TextureObjectManager.cpp
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- ** Copyright 2006, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- **     http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT 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 <stdio.h>
-#include <stdlib.h>
-#include "context.h"
-#include "TextureObjectManager.h"
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-EGLTextureObject::EGLTextureObject()
-    : mSize(0)
-{
-    init();
-}
-
-EGLTextureObject::~EGLTextureObject()
-{
-    if (!direct) {
-        if (mSize && surface.data)
-            free(surface.data);
-        if (mMipmaps)
-            freeMipmaps();
-    }
-}
-
-void EGLTextureObject::init()
-{
-    memset(&surface, 0, sizeof(surface));
-    surface.version = sizeof(surface);
-    mMipmaps = 0;
-    mNumExtraLod = 0;
-    mIsComplete = false;
-    wraps = GL_REPEAT;
-    wrapt = GL_REPEAT;
-    min_filter = GL_LINEAR;
-    mag_filter = GL_LINEAR;
-    internalformat = 0;
-    memset(crop_rect, 0, sizeof(crop_rect));
-    generate_mipmap = GL_FALSE;
-    direct = GL_FALSE;
-    buffer = 0;
-}
-
-void EGLTextureObject::copyParameters(const sp<EGLTextureObject>& old)
-{
-    wraps = old->wraps;
-    wrapt = old->wrapt;
-    min_filter = old->min_filter;
-    mag_filter = old->mag_filter;
-    memcpy(crop_rect, old->crop_rect, sizeof(crop_rect));
-    generate_mipmap = old->generate_mipmap;
-    direct = old->direct;
-}
-
-status_t EGLTextureObject::allocateMipmaps()
-{
-    // here, by construction, mMipmaps=0 && mNumExtraLod=0
-
-    if (!surface.data)
-        return NO_INIT;
-
-    int w = surface.width;
-    int h = surface.height;
-    const int numLods = 31 - gglClz(max(w,h));
-    if (numLods <= 0)
-        return NO_ERROR;
-
-    mMipmaps = (GGLSurface*)malloc(numLods * sizeof(GGLSurface));
-    if (!mMipmaps)
-        return NO_MEMORY;
-
-    memset(mMipmaps, 0, numLods * sizeof(GGLSurface));
-    mNumExtraLod = numLods;
-    return NO_ERROR;
-}
-
-void EGLTextureObject::freeMipmaps()
-{
-    if (mMipmaps) {
-        for (int i=0 ; i<mNumExtraLod ; i++) {
-            if (mMipmaps[i].data) {
-                free(mMipmaps[i].data);
-            }
-        }
-        free(mMipmaps);
-        mMipmaps = 0;
-        mNumExtraLod = 0;
-    }
-}
-
-const GGLSurface& EGLTextureObject::mip(int lod) const
-{
-    if (lod<=0 || !mMipmaps)
-        return surface;
-    lod = min(lod-1, mNumExtraLod-1);
-    return mMipmaps[lod];
-}
-
-GGLSurface& EGLTextureObject::editMip(int lod)
-{
-    return const_cast<GGLSurface&>(mip(lod));
-}
-
-status_t EGLTextureObject::setSurface(GGLSurface const* s)
-{
-    // XXX: glFlush() on 's'
-    if (mSize && surface.data) {
-        free(surface.data);
-    }
-    surface = *s;
-    internalformat = 0;
-    buffer = 0;
-
-    // we should keep the crop_rect, but it's delicate because
-    // the new size of the surface could make it invalid.
-    // so for now, we just loose it.
-    memset(crop_rect, 0, sizeof(crop_rect));
-
-    // it would be nice if we could keep the generate_mipmap flag,
-    // we would have to generate them right now though.
-    generate_mipmap = GL_FALSE;
-
-    direct = GL_TRUE;
-    mSize = 0;  // we don't own this surface
-    if (mMipmaps)
-        freeMipmaps();
-    mIsComplete = true;
-    return NO_ERROR;
-}
-
-status_t EGLTextureObject::setImage(ANativeWindowBuffer* native_buffer)
-{
-    GGLSurface sur;
-    sur.version = sizeof(GGLSurface);
-    sur.width = native_buffer->width;
-    sur.height= native_buffer->height;
-    sur.stride= native_buffer->stride;
-    sur.format= native_buffer->format;
-    sur.data  = 0;
-    setSurface(&sur);
-    buffer = native_buffer;
-    return NO_ERROR;
-}
-
-status_t EGLTextureObject::reallocate(
-        GLint level, int w, int h, int s,
-        int format, int compressedFormat, int bpr)
-{
-    const size_t size = h * bpr;
-    if (level == 0)
-    {
-        if (size!=mSize || !surface.data) {
-            if (mSize && surface.data) {
-                free(surface.data);
-            }
-            surface.data = (GGLubyte*)malloc(size);
-            if (!surface.data) {
-                mSize = 0;
-                mIsComplete = false;
-                return NO_MEMORY;
-            }
-            mSize = size;
-        }
-        surface.version = sizeof(GGLSurface);
-        surface.width  = w;
-        surface.height = h;
-        surface.stride = s;
-        surface.format = format;
-        surface.compressedFormat = compressedFormat;
-        if (mMipmaps)
-            freeMipmaps();
-        mIsComplete = true;
-    }
-    else
-    {
-        if (!mMipmaps) {
-            if (allocateMipmaps() != NO_ERROR)
-                return NO_MEMORY;
-        }
-
-        ALOGW_IF(level-1 >= mNumExtraLod,
-                "specifying mipmap level %d, but # of level is %d",
-                level, mNumExtraLod+1);
-
-        GGLSurface& mipmap = editMip(level);
-        if (mipmap.data)
-            free(mipmap.data);
-
-        mipmap.data = (GGLubyte*)malloc(size);
-        if (!mipmap.data) {
-            memset(&mipmap, 0, sizeof(GGLSurface));
-            mIsComplete = false;
-            return NO_MEMORY;
-        }
-
-        mipmap.version = sizeof(GGLSurface);
-        mipmap.width  = w;
-        mipmap.height = h;
-        mipmap.stride = s;
-        mipmap.format = format;
-        mipmap.compressedFormat = compressedFormat;
-
-        // check if the texture is complete
-        mIsComplete = true;
-        const GGLSurface* prev = &surface;
-        for (int i=0 ; i<mNumExtraLod ; i++) {
-            const GGLSurface* curr = mMipmaps + i;
-            if (curr->format != surface.format) {
-                mIsComplete = false;
-                break;
-            }
-
-            uint32_t w = (prev->width  >> 1) ? : 1;
-            uint32_t h = (prev->height >> 1) ? : 1;
-            if (w != curr->width || h != curr->height) {
-                mIsComplete = false;
-                break;
-            }
-            prev = curr;
-        }
-    }
-    return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-EGLSurfaceManager::EGLSurfaceManager()
-    : TokenManager()
-{
-}
-
-EGLSurfaceManager::~EGLSurfaceManager()
-{
-    // everything gets freed automatically here...
-}
-
-sp<EGLTextureObject> EGLSurfaceManager::createTexture(GLuint name)
-{
-    sp<EGLTextureObject> result;
-
-    Mutex::Autolock _l(mLock);
-    if (mTextures.indexOfKey(name) >= 0)
-        return result; // already exists!
-
-    result = new EGLTextureObject();
-
-    status_t err = mTextures.add(name, result);
-    if (err < 0)
-        result.clear();
-
-    return result;
-}
-
-sp<EGLTextureObject> EGLSurfaceManager::removeTexture(GLuint name)
-{
-    Mutex::Autolock _l(mLock);
-    const ssize_t index = mTextures.indexOfKey(name);
-    if (index >= 0) {
-        sp<EGLTextureObject> result(mTextures.valueAt(index));
-        mTextures.removeItemsAt(index);
-        return result;
-    }
-    return 0;
-}
-
-sp<EGLTextureObject> EGLSurfaceManager::replaceTexture(GLuint name)
-{
-    sp<EGLTextureObject> tex;
-    Mutex::Autolock _l(mLock);
-    const ssize_t index = mTextures.indexOfKey(name);
-    if (index >= 0) {
-        const sp<EGLTextureObject>& old = mTextures.valueAt(index);
-        const uint32_t refs = old->getStrongCount();
-        if (ggl_likely(refs == 1)) {
-            // we're the only owner
-            tex = old;
-        } else {
-            // keep the texture's parameters
-            tex = new EGLTextureObject();
-            tex->copyParameters(old);
-            mTextures.removeItemsAt(index);
-            mTextures.add(name, tex);
-        }
-    }
-    return tex;
-}
-
-void EGLSurfaceManager::deleteTextures(GLsizei n, const GLuint *tokens)
-{
-    // free all textures
-    Mutex::Autolock _l(mLock);
-    for (GLsizei i=0 ; i<n ; i++) {
-        const GLuint t(*tokens++);
-        if (t) {
-            mTextures.removeItem(t);
-        }
-    }
-}
-
-sp<EGLTextureObject> EGLSurfaceManager::texture(GLuint name)
-{
-    Mutex::Autolock _l(mLock);
-    const ssize_t index = mTextures.indexOfKey(name);
-    if (index >= 0)
-        return mTextures.valueAt(index);
-    return 0;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h
deleted file mode 100644
index 9cf8771..0000000
--- a/opengl/libagl/TextureObjectManager.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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_OPENGLES_SURFACE_H
-#define ANDROID_OPENGLES_SURFACE_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-#include <cutils/atomic.h>
-#include <utils/threads.h>
-#include <utils/RefBase.h>
-#include <utils/KeyedVector.h>
-#include <utils/Errors.h>
-
-#include <private/pixelflinger/ggl_context.h>
-
-#include <GLES/gl.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-#include "Tokenizer.h"
-#include "TokenManager.h"
-
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class EGLTextureObject : public LightRefBase<EGLTextureObject>
-{
-public:
-                    EGLTextureObject();
-                   ~EGLTextureObject();
-
-    status_t    setSurface(GGLSurface const* s);
-    status_t    setImage(ANativeWindowBuffer* buffer);
-    void        setImageBits(void* vaddr) { surface.data = (GGLubyte*)vaddr; }
-
-    status_t            reallocate(GLint level,
-                            int w, int h, int s,
-                            int format, int compressedFormat, int bpr);
-    inline  size_t      size() const { return mSize; }
-    const GGLSurface&   mip(int lod) const;
-    GGLSurface&         editMip(int lod);
-    bool                hasMipmaps() const { return mMipmaps!=0; }
-    bool                isComplete() const { return mIsComplete; }
-    void                copyParameters(const sp<EGLTextureObject>& old);
-
-private:
-        status_t        allocateMipmaps();
-            void        freeMipmaps();
-            void        init();
-    size_t              mSize;
-    GGLSurface          *mMipmaps;
-    int                 mNumExtraLod;
-    bool                mIsComplete;
-
-public:
-    GGLSurface          surface;
-    GLenum              wraps;
-    GLenum              wrapt;
-    GLenum              min_filter;
-    GLenum              mag_filter;
-    GLenum              internalformat;
-    GLint               crop_rect[4];
-    GLint               generate_mipmap;
-    GLint               direct;
-    ANativeWindowBuffer* buffer;
-};
-
-// ----------------------------------------------------------------------------
-
-class EGLSurfaceManager :
-    public LightRefBase<EGLSurfaceManager>,
-    public TokenManager
-{
-public:
-                EGLSurfaceManager();
-                ~EGLSurfaceManager();
-
-    sp<EGLTextureObject>    createTexture(GLuint name);
-    sp<EGLTextureObject>    removeTexture(GLuint name);
-    sp<EGLTextureObject>    replaceTexture(GLuint name);
-    void                    deleteTextures(GLsizei n, const GLuint *tokens);
-    sp<EGLTextureObject>    texture(GLuint name);
-
-private:
-    mutable Mutex                               mLock;
-    KeyedVector< GLuint, sp<EGLTextureObject> > mTextures;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_SURFACE_H
-
diff --git a/opengl/libagl/TokenManager.cpp b/opengl/libagl/TokenManager.cpp
deleted file mode 100644
index eea6025..0000000
--- a/opengl/libagl/TokenManager.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/* libs/opengles/surface.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 <stdio.h>
-#include <stdlib.h>
-#include "TokenManager.h"
-
-namespace android {
-// ----------------------------------------------------------------------------
-
-TokenManager::TokenManager()
-{
-    // token 0 is always reserved
-    mTokenizer.reserve(0);
-}
-
-TokenManager::~TokenManager()
-{
-}
-
-status_t TokenManager::getToken(GLsizei n, GLuint *tokens)
-{
-    Mutex::Autolock _l(mLock);
-    for (GLsizei i=0 ; i<n ; i++)
-        *tokens++ = mTokenizer.acquire();
-    return NO_ERROR;
-}
-
-void TokenManager::recycleTokens(GLsizei n, const GLuint *tokens)
-{
-    Mutex::Autolock _l(mLock);
-    for (int i=0 ; i<n ; i++) {
-        const GLuint token = *tokens++;
-        if (token) {
-            mTokenizer.release(token);
-        }
-    }
-}
-
-bool TokenManager::isTokenValid(GLuint token) const
-{
-    Mutex::Autolock _l(mLock);
-    return mTokenizer.isAcquired(token);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
diff --git a/opengl/libagl/TokenManager.h b/opengl/libagl/TokenManager.h
deleted file mode 100644
index 49c1469..0000000
--- a/opengl/libagl/TokenManager.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_TOKEN_MANAGER_H
-#define ANDROID_OPENGLES_TOKEN_MANAGER_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-
-#include <GLES/gl.h>
-
-#include "Tokenizer.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class TokenManager
-{
-public:
-                TokenManager();
-                ~TokenManager();
-
-    status_t    getToken(GLsizei n, GLuint *tokens);
-    void        recycleTokens(GLsizei n, const GLuint *tokens);
-    bool        isTokenValid(GLuint token) const;
-
-private:
-    mutable Mutex   mLock;
-    Tokenizer       mTokenizer;
-};
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_TOKEN_MANAGER_H
-
diff --git a/opengl/libagl/Tokenizer.cpp b/opengl/libagl/Tokenizer.cpp
deleted file mode 100644
index ac0a48c..0000000
--- a/opengl/libagl/Tokenizer.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/* libs/opengles/Tokenizer.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 <stdio.h>
-
-#include "Tokenizer.h"
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t)
-
-Tokenizer::Tokenizer()
-{
-}
-
-Tokenizer::Tokenizer(const Tokenizer& other)
-    : mRanges(other.mRanges)
-{
-}
-
-Tokenizer::~Tokenizer()
-{
-}
-
-uint32_t Tokenizer::acquire()
-{
-    if (!mRanges.size() || mRanges[0].first) {
-        _insertTokenAt(0,0);
-        return 0;
-    }
-    
-    // just extend the first run
-    const run_t& run = mRanges[0];
-    uint32_t token = run.first + run.length;
-    _insertTokenAt(token, 1);
-    return token;
-}
-
-bool Tokenizer::isAcquired(uint32_t token) const
-{
-    return (_indexOrderOf(token) >= 0);
-}
-
-status_t Tokenizer::reserve(uint32_t token)
-{
-    size_t o;
-    const ssize_t i = _indexOrderOf(token, &o);
-    if (i >= 0) {
-        return BAD_VALUE; // this token is already taken
-    }
-    ssize_t err = _insertTokenAt(token, o);
-    return (err<0) ? err : status_t(NO_ERROR);
-}
-
-status_t Tokenizer::release(uint32_t token)
-{
-    const ssize_t i = _indexOrderOf(token);
-    if (i >= 0) {
-        const run_t& run = mRanges[i];
-        if ((token >= run.first) && (token < run.first+run.length)) {
-            // token in this range, we need to split
-            run_t& run = mRanges.editItemAt(i);
-            if ((token == run.first) || (token == run.first+run.length-1)) {
-                if (token == run.first) {
-                    run.first += 1;
-                }
-                run.length -= 1;
-                if (run.length == 0) {
-                    // XXX: should we systematically remove a run that's empty?
-                    mRanges.removeItemsAt(i);
-                }
-            } else {
-                // split the run
-                run_t new_run;
-                new_run.first = token+1;
-                new_run.length = run.first+run.length - new_run.first;
-                run.length = token - run.first;
-                mRanges.insertAt(new_run, i+1);
-            }
-            return NO_ERROR;
-        }
-    }
-    return NAME_NOT_FOUND;
-}
-
-ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const
-{
-    // binary search
-    ssize_t err = NAME_NOT_FOUND;
-    ssize_t l = 0;
-    ssize_t h = mRanges.size()-1;
-    ssize_t mid;
-    const run_t* a = mRanges.array();
-    while (l <= h) {
-        mid = l + (h - l)/2;
-        const run_t* const curr = a + mid;
-        int c = 0;
-        if (token < curr->first)                        c = 1;
-        else if (token >= curr->first+curr->length)     c = -1;
-        if (c == 0) {
-            err = l = mid;
-            break;
-        } else if (c < 0) {
-            l = mid + 1;
-        } else {
-            h = mid - 1;
-        }
-    }
-    if (order) *order = l;
-    return err;
-}
-
-ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index)
-{
-    const size_t c = mRanges.size();
-
-    if (index >= 1) {
-        // do we need to merge with the previous run?
-        run_t& p = mRanges.editItemAt(index-1);
-        if (p.first+p.length == token) {
-            p.length += 1;
-            if (index < c) {
-                const run_t& n = mRanges[index];
-                if (token+1 == n.first) {
-                    p.length += n.length;
-                    mRanges.removeItemsAt(index);
-                }
-            }
-            return index;
-        }
-    }
-    
-    if (index < c) {
-        // do we need to merge with the next run?
-        run_t& n = mRanges.editItemAt(index);
-        if (token+1 == n.first) {
-            n.first -= 1;
-            n.length += 1;
-            return index;
-        }
-    }
-
-    return mRanges.insertAt(run_t(token,1), index);
-}
-
-void Tokenizer::dump() const
-{
-    const run_t* ranges = mRanges.array();
-    const size_t c = mRanges.size();
-    ALOGD("Tokenizer (%p, size = %zu)\n", this, c);
-    for (size_t i=0 ; i<c ; i++) {
-        ALOGD("%zu: (%u, %u)\n", i, ranges[i].first, ranges[i].length);
-    }
-}
-
-}; // namespace android
-
diff --git a/opengl/libagl/Tokenizer.h b/opengl/libagl/Tokenizer.h
deleted file mode 100644
index ac555cb..0000000
--- a/opengl/libagl/Tokenizer.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/* libs/opengles/Tokenizer.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_TOKENIZER_H
-#define ANDROID_OPENGLES_TOKENIZER_H
-
-#include <utils/Vector.h>
-#include <utils/Errors.h>
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-class Tokenizer
-{
-public:
-                Tokenizer();
-                Tokenizer(const Tokenizer& other);
-                ~Tokenizer();
-
-    uint32_t    acquire();
-    status_t    reserve(uint32_t token);
-    status_t    release(uint32_t token);
-    bool        isAcquired(uint32_t token) const;
-
-    void dump() const;
-
-    struct run_t {
-        run_t() {};
-        run_t(uint32_t f, uint32_t l) : first(f), length(l) {}
-        uint32_t    first;
-        uint32_t    length;
-    };
-private:
-    ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const;
-    ssize_t _insertTokenAt(uint32_t token, size_t index);
-    Vector<run_t>   mRanges;
-};
-
-}; // namespace android
-
-// ----------------------------------------------------------------------------
-
-#endif // ANDROID_OPENGLES_TOKENIZER_H
diff --git a/opengl/libagl/arch-mips/fixed_asm.S b/opengl/libagl/arch-mips/fixed_asm.S
deleted file mode 100644
index a30ffc5..0000000
--- a/opengl/libagl/arch-mips/fixed_asm.S
+++ /dev/null
@@ -1,61 +0,0 @@
-/* libs/opengles/arch-mips/fixed_asm.S
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-
-    .text
-    .align 4
-
-/*
- * this version rounds-to-nearest and saturates numbers
- * outside the range (but not NaNs).
- */
-
-	.global	gglFloatToFixed
-	.ent	gglFloatToFixed
-	.type	gglFloatToFixed, @function
-gglFloatToFixed:
-#if !defined(__mips_soft_float)
-	mfc1	$a0,$f12
-#endif
-	srl	$t0,$a0,31		/* t0 <- sign bit */
-	srl	$t1,$a0,23
-	andi	$t1,$t1,0xff		/* get the e */
-	li	$t2,0x8e
-	subu	$t1,$t2,$t1		/* t1=127+15-e */
-	blez	$t1,0f			/* t1<=0? */
-	sll	$t2,$a0,8		/* mantissa<<8 */
-	lui	$t3,0x8000
-	or	$t2,$t2,$t3		/* add the missing 1 */
-	subu	$t1,$t1,1
-	srl	$v0,$t2,$t1
-	sltiu	$t3,$t1,32		/* t3=1 if t1<32, else t3=0. t1>=32 means the float value is too small. */
-	andi	$t4,$v0,0x1
-	srl	$v0,$v0,1		/* scale to 16.16 */
-	addu	$v0,$v0,$t4		/* round-to-nearest */
-	subu	$t2,$zero,$v0
-	movn	$v0,$t2,$t0		/* if negative? */
-	or	$t1,$a0,$zero		/* a0=0? */
-	movz	$v0,$zero,$t1
-	movz	$v0,$zero,$t3		/* t3=0 then res=0 */
-	jr	$ra
-0:
-	lui	$t1,0x8000
-	and	$v0,$a0,$t1		/* keep only the sign bit */
-	li	$t1,0x7fffffff
-	movz	$v0,$t1,$t0		/* positive, maximum value */
-	jr	$ra
-	.end	gglFloatToFixed
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
deleted file mode 100644
index 2d36c61..0000000
--- a/opengl/libagl/array.cpp
+++ /dev/null
@@ -1,1590 +0,0 @@
-/*
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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 <stdlib.h>
-#include <stdio.h>
-
-#include "context.h"
-#include "fp.h"
-#include "state.h"
-#include "matrix.h"
-#include "vertex.h"
-#include "light.h"
-#include "primitives.h"
-#include "texture.h"
-#include "BufferObjectManager.h"
-
-// ----------------------------------------------------------------------------
-
-#define VC_CACHE_STATISTICS     0
-#define VC_CACHE_TYPE_NONE      0
-#define VC_CACHE_TYPE_INDEXED   1
-#define VC_CACHE_TYPE_LRU       2
-#define VC_CACHE_TYPE           VC_CACHE_TYPE_INDEXED
-
-#if VC_CACHE_STATISTICS
-#include <utils/Timers.h>
-#endif
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-static void validate_arrays(ogles_context_t* c, GLenum mode);
-
-static void compileElements__generic(ogles_context_t*,
-        vertex_t*, GLint, GLsizei);
-static void compileElement__generic(ogles_context_t*,
-        vertex_t*, GLint);
-
-static void drawPrimitivesPoints(ogles_context_t*, GLint, GLsizei);
-static void drawPrimitivesLineStrip(ogles_context_t*, GLint, GLsizei);
-static void drawPrimitivesLineLoop(ogles_context_t*, GLint, GLsizei);
-static void drawPrimitivesLines(ogles_context_t*, GLint, GLsizei);
-static void drawPrimitivesTriangleStrip(ogles_context_t*, GLint, GLsizei);
-static void drawPrimitivesTriangleFan(ogles_context_t*, GLint, GLsizei);
-static void drawPrimitivesTriangles(ogles_context_t*, GLint, GLsizei);
-
-static void drawIndexedPrimitivesPoints(ogles_context_t*,
-        GLsizei, const GLvoid*);
-static void drawIndexedPrimitivesLineStrip(ogles_context_t*,
-        GLsizei, const GLvoid*);
-static void drawIndexedPrimitivesLineLoop(ogles_context_t*,
-        GLsizei, const GLvoid*);
-static void drawIndexedPrimitivesLines(ogles_context_t*,
-        GLsizei, const GLvoid*);
-static void drawIndexedPrimitivesTriangleStrip(ogles_context_t*,
-        GLsizei, const GLvoid*);
-static void drawIndexedPrimitivesTriangleFan(ogles_context_t*,
-        GLsizei, const GLvoid*);
-static void drawIndexedPrimitivesTriangles(ogles_context_t*,
-        GLsizei, const GLvoid*);
-
-// ----------------------------------------------------------------------------
-
-typedef void (*arrays_prims_fct_t)(ogles_context_t*, GLint, GLsizei);
-static const arrays_prims_fct_t drawArraysPrims[] = {
-    drawPrimitivesPoints,
-    drawPrimitivesLines,
-    drawPrimitivesLineLoop,
-    drawPrimitivesLineStrip,
-    drawPrimitivesTriangles,
-    drawPrimitivesTriangleStrip,
-    drawPrimitivesTriangleFan
-};
-
-typedef void (*elements_prims_fct_t)(ogles_context_t*, GLsizei, const GLvoid*);
-static const elements_prims_fct_t drawElementsPrims[] = {
-    drawIndexedPrimitivesPoints,
-    drawIndexedPrimitivesLines,
-    drawIndexedPrimitivesLineLoop,
-    drawIndexedPrimitivesLineStrip,
-    drawIndexedPrimitivesTriangles,
-    drawIndexedPrimitivesTriangleStrip,
-    drawIndexedPrimitivesTriangleFan
-};
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-void ogles_init_array(ogles_context_t* c)
-{
-    c->arrays.vertex.size = 4;
-    c->arrays.vertex.type = GL_FLOAT;
-    c->arrays.color.size = 4;
-    c->arrays.color.type = GL_FLOAT;
-    c->arrays.normal.size = 4;
-    c->arrays.normal.type = GL_FLOAT;
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        c->arrays.texture[i].size = 4;
-        c->arrays.texture[i].type = GL_FLOAT;
-    }
-    c->vc.init();
-
-    if (!c->vc.vBuffer) {
-        // this could have failed
-        ogles_error(c, GL_OUT_OF_MEMORY);
-    }
-}
-
-void ogles_uninit_array(ogles_context_t* c)
-{
-    c->vc.uninit();
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Array fetchers
-#endif
-
-static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) {
-    memcpy(v, c->current.color.v, sizeof(vec4_t));
-}
-static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) {
-    memcpy(v, c->currentNormal.v, sizeof(vec3_t));
-}
-static void currentTexCoord(ogles_context_t* c, GLfixed* v, const GLvoid*) {
-    memcpy(v, c->current.texture[c->arrays.tmu].v, sizeof(vec4_t));
-}
-
-
-static void fetchNop(ogles_context_t*, GLfixed*, const GLvoid*) {
-}
-static void fetch2b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
-    v[0] = gglIntToFixed(p[0]);
-    v[1] = gglIntToFixed(p[1]);
-}
-static void fetch2s(ogles_context_t*, GLfixed* v, const GLshort* p) {
-    v[0] = gglIntToFixed(p[0]);
-    v[1] = gglIntToFixed(p[1]);
-}
-static void fetch2x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
-    memcpy(v, p, 2*sizeof(GLfixed));
-}
-static void fetch2f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
-    v[0] = gglFloatToFixed(p[0]);
-    v[1] = gglFloatToFixed(p[1]);
-}
-static void fetch3b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
-    v[0] = gglIntToFixed(p[0]);
-    v[1] = gglIntToFixed(p[1]);
-    v[2] = gglIntToFixed(p[2]);
-}
-static void fetch3s(ogles_context_t*, GLfixed* v, const GLshort* p) {
-    v[0] = gglIntToFixed(p[0]);
-    v[1] = gglIntToFixed(p[1]);
-    v[2] = gglIntToFixed(p[2]);
-}
-static void fetch3x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
-    memcpy(v, p, 3*sizeof(GLfixed));
-}
-static void fetch3f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
-    v[0] = gglFloatToFixed(p[0]);
-    v[1] = gglFloatToFixed(p[1]);
-    v[2] = gglFloatToFixed(p[2]);
-}
-static void fetch4b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
-    v[0] = gglIntToFixed(p[0]);
-    v[1] = gglIntToFixed(p[1]);
-    v[2] = gglIntToFixed(p[2]);
-    v[3] = gglIntToFixed(p[3]);
-}
-static void fetch4s(ogles_context_t*, GLfixed* v, const GLshort* p) {
-    v[0] = gglIntToFixed(p[0]);
-    v[1] = gglIntToFixed(p[1]);
-    v[2] = gglIntToFixed(p[2]);
-    v[3] = gglIntToFixed(p[3]);
-}
-static void fetch4x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
-    memcpy(v, p, 4*sizeof(GLfixed));
-}
-static void fetch4f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
-    v[0] = gglFloatToFixed(p[0]);
-    v[1] = gglFloatToFixed(p[1]);
-    v[2] = gglFloatToFixed(p[2]);
-    v[3] = gglFloatToFixed(p[3]);
-}
-static void fetchExpand4ub(ogles_context_t*, GLfixed* v, const GLubyte* p) {
-    v[0] = GGL_UB_TO_X(p[0]);
-    v[1] = GGL_UB_TO_X(p[1]);
-    v[2] = GGL_UB_TO_X(p[2]);
-    v[3] = GGL_UB_TO_X(p[3]);
-}
-static void fetchClamp4x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
-    v[0] = gglClampx(p[0]);
-    v[1] = gglClampx(p[1]);
-    v[2] = gglClampx(p[2]);
-    v[3] = gglClampx(p[3]);
-}
-static void fetchClamp4f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
-    v[0] = gglClampx(gglFloatToFixed(p[0]));
-    v[1] = gglClampx(gglFloatToFixed(p[1]));
-    v[2] = gglClampx(gglFloatToFixed(p[2]));
-    v[3] = gglClampx(gglFloatToFixed(p[3]));
-}
-static void fetchExpand3ub(ogles_context_t*, GLfixed* v, const GLubyte* p) {
-    v[0] = GGL_UB_TO_X(p[0]);
-    v[1] = GGL_UB_TO_X(p[1]);
-    v[2] = GGL_UB_TO_X(p[2]);
-    v[3] = 0x10000;
-}
-static void fetchClamp3x(ogles_context_t*, GLfixed* v, const GLfixed* p) {
-    v[0] = gglClampx(p[0]);
-    v[1] = gglClampx(p[1]);
-    v[2] = gglClampx(p[2]);
-    v[3] = 0x10000;
-}
-static void fetchClamp3f(ogles_context_t*, GLfixed* v, const GLfloat* p) {
-    v[0] = gglClampx(gglFloatToFixed(p[0]));
-    v[1] = gglClampx(gglFloatToFixed(p[1]));
-    v[2] = gglClampx(gglFloatToFixed(p[2]));
-    v[3] = 0x10000;
-}
-static void fetchExpand3b(ogles_context_t*, GLfixed* v, const GLbyte* p) {
-    v[0] = GGL_B_TO_X(p[0]);
-    v[1] = GGL_B_TO_X(p[1]);
-    v[2] = GGL_B_TO_X(p[2]);
-}
-static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) {
-    v[0] = GGL_S_TO_X(p[0]);
-    v[1] = GGL_S_TO_X(p[1]);
-    v[2] = GGL_S_TO_X(p[2]);
-}
-
-typedef array_t::fetcher_t fn_t;
-
-static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x}
-    { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0,
-         (fn_t)fetch3f, 0, 0, 0, 0, 0,
-         (fn_t)fetch3x },
-    { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0,
-         (fn_t)fetch4f, 0, 0, 0, 0, 0,
-         (fn_t)fetch4x },
-};
-static const fn_t color_clamp_fct[2][16] = { // size={3,4}, type={ub,f,x}
-    { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0,
-         (fn_t)fetchClamp3f, 0, 0, 0, 0, 0,
-         (fn_t)fetchClamp3x },
-    { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0,
-         (fn_t)fetchClamp4f, 0, 0, 0, 0, 0,
-         (fn_t)fetchClamp4x },
-};
-static const fn_t normal_fct[1][16] = { // size={3}, type={b,s,f,x}
-    { (fn_t)fetchExpand3b, 0,
-      (fn_t)fetchExpand3s, 0, 0, 0,
-      (fn_t)fetch3f, 0, 0, 0, 0, 0,
-      (fn_t)fetch3x },
-};
-static const fn_t vertex_fct[3][16] = { // size={2,3,4}, type={b,s,f,x}
-    { (fn_t)fetch2b, 0,
-      (fn_t)fetch2s, 0, 0, 0,
-      (fn_t)fetch2f, 0, 0, 0, 0, 0,
-      (fn_t)fetch3x },
-    { (fn_t)fetch3b, 0,
-      (fn_t)fetch3s, 0, 0, 0,
-      (fn_t)fetch3f, 0, 0, 0, 0, 0,
-      (fn_t)fetch3x },
-    { (fn_t)fetch4b, 0,
-      (fn_t)fetch4s, 0, 0, 0,
-      (fn_t)fetch4f, 0, 0, 0, 0, 0,
-      (fn_t)fetch4x }
-};
-static const fn_t texture_fct[3][16] = { // size={2,3,4}, type={b,s,f,x}
-    { (fn_t)fetch2b, 0,
-      (fn_t)fetch2s, 0, 0, 0,
-      (fn_t)fetch2f, 0, 0, 0, 0, 0,
-      (fn_t)fetch2x },
-    { (fn_t)fetch3b, 0,
-      (fn_t)fetch3s, 0, 0, 0,
-      (fn_t)fetch3f, 0, 0, 0, 0, 0,
-      (fn_t)fetch3x },
-    { (fn_t)fetch4b, 0,
-      (fn_t)fetch4s, 0, 0, 0,
-      (fn_t)fetch4f, 0, 0, 0, 0, 0,
-      (fn_t)fetch4x }
-};
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark array_t
-#endif
-
-void array_t::init(
-        GLint size, GLenum type, GLsizei stride,
-        const GLvoid *pointer, const buffer_t* bo, GLsizei count)
-{
-    if (!stride) {
-        stride = size;
-        switch (type) {
-        case GL_SHORT:
-        case GL_UNSIGNED_SHORT:
-            stride *= 2;
-            break;
-        case GL_FLOAT:
-        case GL_FIXED:
-            stride *= 4;
-            break;
-        }
-    }
-    this->size = size;
-    this->type = type;
-    this->stride = stride;
-    this->pointer = pointer;
-    this->bo = bo;
-    this->bounds = count;
-}
-
-inline void array_t::resolve()
-{
-    physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer;
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark vertex_cache_t
-#endif
-
-void vertex_cache_t::init()
-{
-    // make sure the size of vertex_t allows cache-line alignment
-    CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize;
-    (void)assertAlignedSize; // suppress unused warning.
-
-    const int align = 32;
-    const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
-    const size_t size = s*sizeof(vertex_t) + align;
-    base = malloc(size);
-    if (base) {
-        memset(base, 0, size);
-        vBuffer = (vertex_t*)((size_t(base) + align - 1) & ~(align-1));
-        vCache = vBuffer + VERTEX_BUFFER_SIZE;
-        sequence = 0;
-    }
-}
-
-void vertex_cache_t::uninit()
-{
-    free(base);
-    base = vBuffer = vCache = 0;
-}
-
-void vertex_cache_t::clear()
-{
-#if VC_CACHE_STATISTICS
-    startTime = systemTime(SYSTEM_TIME_THREAD);
-    total = 0;
-    misses = 0;
-#endif
-
-#if VC_CACHE_TYPE == VC_CACHE_TYPE_LRU
-    vertex_t* v = vBuffer;
-    size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
-    do {
-        v->mru = 0;
-        v++;
-    } while (--count);
-#endif
-
-    sequence += INDEX_SEQ;
-    if (sequence >= 0x80000000LU) {
-        sequence = INDEX_SEQ;
-        vertex_t* v = vBuffer;
-        size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE;
-        do {
-            v->index = 0;
-            v++;
-        } while (--count);
-    }
-}
-
-#if VC_CACHE_STATISTICS
-void vertex_cache_t::dump_stats(GLenum mode)
-{
-    nsecs_t time = systemTime(SYSTEM_TIME_THREAD) - startTime;
-    uint32_t hits = total - misses;
-    uint32_t prim_count;
-    switch (mode) {
-    case GL_POINTS:             prim_count = total;         break;
-    case GL_LINE_STRIP:         prim_count = total - 1;     break;
-    case GL_LINE_LOOP:          prim_count = total - 1;     break;
-    case GL_LINES:              prim_count = total / 2;     break;
-    case GL_TRIANGLE_STRIP:     prim_count = total - 2;     break;
-    case GL_TRIANGLE_FAN:       prim_count = total - 2;     break;
-    case GL_TRIANGLES:          prim_count = total / 3;     break;
-    default:    return;
-    }
-    printf( "total=%5u, hits=%5u, miss=%5u, hitrate=%3u%%,"
-            " prims=%5u, time=%6u us, prims/s=%d, v/t=%f\n",
-            total, hits, misses, (hits*100)/total,
-            prim_count, int(ns2us(time)), int(prim_count*float(seconds(1))/time),
-            float(misses) / prim_count);
-}
-#else
-void vertex_cache_t::dump_stats(GLenum /*mode*/)
-{
-}
-#endif
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-static __attribute__((noinline))
-void enableDisableClientState(ogles_context_t* c, GLenum array, bool enable)
-{
-    const int tmu = c->arrays.activeTexture;
-    array_t* a;
-    switch (array) {
-    case GL_COLOR_ARRAY:            a = &c->arrays.color;           break;
-    case GL_NORMAL_ARRAY:           a = &c->arrays.normal;          break;
-    case GL_TEXTURE_COORD_ARRAY:    a = &c->arrays.texture[tmu];    break;
-    case GL_VERTEX_ARRAY:           a = &c->arrays.vertex;          break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    a->enable = enable ? GL_TRUE : GL_FALSE;
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Vertex Cache
-#endif
-
-static __attribute__((noinline))
-vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index)
-{
-    #if VC_CACHE_STATISTICS
-        c->vc.misses++;
-    #endif
-    if (ggl_unlikely(v->locked)) {
-        // we're just looking for an entry in the cache that is not locked.
-        // and we know that there cannot be more than 2 locked entries
-        // because a triangle needs at most 3 vertices.
-        // We never use the first and second entries because they might be in
-        // use by the striper or faner. Any other entry will do as long as
-        // it's not locked.
-        // We compute directly the index of a "free" entry from the locked
-        // state of v[2] and v[3].
-        v = c->vc.vBuffer + 2;
-        v += v[0].locked | (v[1].locked<<1);
-    }
-    // note: compileElement clears v->flags
-    c->arrays.compileElement(c, v, index);
-    v->locked = 1;
-    return v;
-}
-
-static __attribute__((noinline))
-vertex_t* fetch_vertex(ogles_context_t* c, size_t index)
-{
-    index |= c->vc.sequence;
-
-#if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED
-
-    vertex_t* const v = c->vc.vCache +
-            (index & (vertex_cache_t::VERTEX_CACHE_SIZE-1));
-
-    if (ggl_likely(v->index == index)) {
-        v->locked = 1;
-        return v;
-    }
-    return cache_vertex(c, v, index);
-
-#elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU
-
-    vertex_t* v = c->vc.vCache +
-            (index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2;
-
-    // always record LRU in v[0]
-    if (ggl_likely(v[0].index == index)) {
-        v[0].locked = 1;
-        v[0].mru = 0;
-        return &v[0];
-    }
-
-    if (ggl_likely(v[1].index == index)) {
-        v[1].locked = 1;
-        v[0].mru = 1;
-        return &v[1];
-    }
-
-    const int lru = 1 - v[0].mru;
-    v[0].mru = lru;
-    return cache_vertex(c, &v[lru], index);
-
-#elif VC_CACHE_TYPE == VC_CACHE_TYPE_NONE
-
-    // just for debugging...
-    vertex_t* v = c->vc.vBuffer + 2;
-    return cache_vertex(c, v, index);
-
-#endif
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Primitive Assembly
-#endif
-
-void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count)
-{
-    if (ggl_unlikely(count < 1))
-        return;
-
-    // vertex cache size must be multiple of 1
-    const GLsizei vcs =
-            (vertex_cache_t::VERTEX_BUFFER_SIZE +
-             vertex_cache_t::VERTEX_CACHE_SIZE);
-    do {
-        vertex_t* v = c->vc.vBuffer;
-        GLsizei num = count > vcs ? vcs : count;
-        c->arrays.cull = vertex_t::CLIP_ALL;
-        c->arrays.compileElements(c, v, first, num);
-        first += num;
-        count -= num;
-        if (!c->arrays.cull) {
-            // quick/trivial reject of the whole batch
-            do {
-                const uint32_t cc = v[0].flags;
-                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-                    c->prims.renderPoint(c, v);
-                v++;
-                num--;
-            } while (num);
-        }
-    } while (count);
-}
-
-// ----------------------------------------------------------------------------
-
-void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count)
-{
-    if (ggl_unlikely(count < 2))
-        return;
-
-    vertex_t *v, *v0, *v1;
-    c->arrays.cull = vertex_t::CLIP_ALL;
-    c->arrays.compileElement(c, c->vc.vBuffer, first);
-    first += 1;
-    count -= 1;
-
-    // vertex cache size must be multiple of 1
-    const GLsizei vcs =
-        (vertex_cache_t::VERTEX_BUFFER_SIZE +
-         vertex_cache_t::VERTEX_CACHE_SIZE - 1);
-    do {
-        v0 = c->vc.vBuffer + 0;
-        v  = c->vc.vBuffer + 1;
-        GLsizei num = count > vcs ? vcs : count;
-        c->arrays.compileElements(c, v, first, num);
-        first += num;
-        count -= num;
-        if (!c->arrays.cull) {
-            // quick/trivial reject of the whole batch
-            do {
-                v1 = v++;
-                const uint32_t cc = v0->flags & v1->flags;
-                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-                    c->prims.renderLine(c, v0, v1);
-                v0 = v1;
-                num--;
-            } while (num);
-        }
-        // copy back the last processed vertex
-        c->vc.vBuffer[0] = *v0;
-        c->arrays.cull = v0->flags & vertex_t::CLIP_ALL;
-    } while (count);
-}
-
-void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count)
-{
-    if (ggl_unlikely(count < 2))
-        return;
-    drawPrimitivesLineStrip(c, first, count);
-    if (ggl_likely(count >= 3)) {
-        vertex_t* v0 = c->vc.vBuffer;
-        vertex_t* v1 = c->vc.vBuffer + 1;
-        c->arrays.compileElement(c, v1, first);
-        const uint32_t cc = v0->flags & v1->flags;
-        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-            c->prims.renderLine(c, v0, v1);
-    }
-}
-
-void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count)
-{
-    if (ggl_unlikely(count < 2))
-        return;
-
-    // vertex cache size must be multiple of 2
-    const GLsizei vcs =
-        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
-        vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2;
-    do {
-        vertex_t* v = c->vc.vBuffer;
-        GLsizei num = count > vcs ? vcs : count;
-        c->arrays.cull = vertex_t::CLIP_ALL;
-        c->arrays.compileElements(c, v, first, num);
-        first += num;
-        count -= num;
-        if (!c->arrays.cull) {
-            // quick/trivial reject of the whole batch
-            num -= 2;
-            do {
-                const uint32_t cc = v[0].flags & v[1].flags;
-                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-                    c->prims.renderLine(c, v, v+1);
-                v += 2;
-                num -= 2;
-            } while (num >= 0);
-        }
-    } while (count >= 2);
-}
-
-// ----------------------------------------------------------------------------
-
-static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c,
-        GLint first, GLsizei count, int winding)
-{
-    // winding == 2 : fan
-    // winding == 1 : strip
-
-    if (ggl_unlikely(count < 3))
-        return;
-
-    vertex_t *v, *v0, *v1, *v2;
-    c->arrays.cull = vertex_t::CLIP_ALL;
-    c->arrays.compileElements(c, c->vc.vBuffer, first, 2);
-    first += 2;
-    count -= 2;
-
-    // vertex cache size must be multiple of 2. This is extremely important
-    // because it allows us to preserve the same winding when the whole
-    // batch is culled. We also need 2 extra vertices in the array, because
-    // we always keep the two first ones.
-    const GLsizei vcs =
-        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
-          vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2;
-    do {
-        v0 = c->vc.vBuffer + 0;
-        v1 = c->vc.vBuffer + 1;
-        v  = c->vc.vBuffer + 2;
-        GLsizei num = count > vcs ? vcs : count;
-        c->arrays.compileElements(c, v, first, num);
-        first += num;
-        count -= num;
-        if (!c->arrays.cull) {
-            // quick/trivial reject of the whole batch
-            do {
-                v2 = v++;
-                const uint32_t cc = v0->flags & v1->flags & v2->flags;
-                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-                    c->prims.renderTriangle(c, v0, v1, v2);
-                swap(((winding^=1) ? v1 : v0), v2);
-                num--;
-            } while (num);
-        }
-        if (count) {
-            v0 = c->vc.vBuffer + 2 + vcs - 2;
-            v1 = c->vc.vBuffer + 2 + vcs - 1;
-            if ((winding&2) == 0) {
-                // for strips copy back the two last compiled vertices
-                c->vc.vBuffer[0] = *v0;
-            }
-            c->vc.vBuffer[1] = *v1;
-            c->arrays.cull = v0->flags & v1->flags & vertex_t::CLIP_ALL;
-        }
-    } while (count > 0);
-}
-
-void drawPrimitivesTriangleStrip(ogles_context_t* c,
-        GLint first, GLsizei count) {
-    drawPrimitivesTriangleFanOrStrip(c, first, count, 1);
-}
-
-void drawPrimitivesTriangleFan(ogles_context_t* c,
-        GLint first, GLsizei count) {
-    drawPrimitivesTriangleFanOrStrip(c, first, count, 2);
-}
-
-void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count)
-{
-    if (ggl_unlikely(count < 3))
-        return;
-
-    // vertex cache size must be multiple of 3
-    const GLsizei vcs =
-        ((vertex_cache_t::VERTEX_BUFFER_SIZE +
-        vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3;
-    do {
-        vertex_t* v = c->vc.vBuffer;
-        GLsizei num = count > vcs ? vcs : count;
-        c->arrays.cull = vertex_t::CLIP_ALL;
-        c->arrays.compileElements(c, v, first, num);
-        first += num;
-        count -= num;
-        if (!c->arrays.cull) {
-            // quick/trivial reject of the whole batch
-            num -= 3;
-            do {
-                const uint32_t cc = v[0].flags & v[1].flags & v[2].flags;
-                if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-                    c->prims.renderTriangle(c, v, v+1, v+2);
-                v += 3;
-                num -= 3;
-            } while (num >= 0);
-        }
-    } while (count >= 3);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-// this looks goofy, but gcc does a great job with this...
-static inline unsigned int read_index(int type, const GLvoid*& p) {
-    unsigned int r;
-    if (type) {
-        r = *(const GLubyte*)p;
-        p = (const GLubyte*)p + 1;
-    } else {
-        r = *(const GLushort*)p;
-        p = (const GLushort*)p + 1;
-    }
-    return r;
-}
-
-// ----------------------------------------------------------------------------
-
-void drawIndexedPrimitivesPoints(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices)
-{
-    if (ggl_unlikely(count < 1))
-        return;
-    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
-    do {
-        vertex_t * v = fetch_vertex(c, read_index(type, indices));
-        if (ggl_likely(!(v->flags & vertex_t::CLIP_ALL)))
-            c->prims.renderPoint(c, v);
-        v->locked = 0;
-        count--;
-    } while(count);
-}
-
-// ----------------------------------------------------------------------------
-
-void drawIndexedPrimitivesLineStrip(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices)
-{
-    if (ggl_unlikely(count < 2))
-        return;
-
-    vertex_t * const v = c->vc.vBuffer;
-    vertex_t* v0 = v;
-    vertex_t* v1;
-
-    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
-    c->arrays.compileElement(c, v0, read_index(type, indices));
-    count -= 1;
-    do {
-        v1 = fetch_vertex(c, read_index(type, indices));
-        const uint32_t cc = v0->flags & v1->flags;
-        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-            c->prims.renderLine(c, v0, v1);
-        v0->locked = 0;
-        v0 = v1;
-        count--;
-    } while (count);
-    v1->locked = 0;
-}
-
-void drawIndexedPrimitivesLineLoop(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices)
-{
-    if (ggl_unlikely(count <= 2)) {
-        drawIndexedPrimitivesLines(c, count, indices);
-        return;
-    }
-
-    vertex_t * const v = c->vc.vBuffer;
-    vertex_t* v0 = v;
-    vertex_t* v1;
-
-    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
-    c->arrays.compileElement(c, v0, read_index(type, indices));
-    count -= 1;
-    do {
-        v1 = fetch_vertex(c, read_index(type, indices));
-        const uint32_t cc = v0->flags & v1->flags;
-        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-            c->prims.renderLine(c, v0, v1);
-        v0->locked = 0;
-        v0 = v1;
-        count--;
-    } while (count);
-    v1->locked = 0;
-
-    v1 = c->vc.vBuffer;
-    const uint32_t cc = v0->flags & v1->flags;
-    if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-        c->prims.renderLine(c, v0, v1);
-}
-
-void drawIndexedPrimitivesLines(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices)
-{
-    if (ggl_unlikely(count < 2))
-        return;
-
-    count -= 2;
-    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
-    do {
-        vertex_t* const v0 = fetch_vertex(c, read_index(type, indices));
-        vertex_t* const v1 = fetch_vertex(c, read_index(type, indices));
-        const uint32_t cc = v0->flags & v1->flags;
-        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-            c->prims.renderLine(c, v0, v1);
-        v0->locked = 0;
-        v1->locked = 0;
-        count -= 2;
-    } while (count >= 0);
-}
-
-// ----------------------------------------------------------------------------
-
-static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices, int winding)
-{
-    // winding == 2 : fan
-    // winding == 1 : strip
-
-    if (ggl_unlikely(count < 3))
-        return;
-
-    vertex_t * const v = c->vc.vBuffer;
-    vertex_t* v0 = v;
-    vertex_t* v1 = v+1;
-    vertex_t* v2;
-
-    const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
-    c->arrays.compileElement(c, v0, read_index(type, indices));
-    c->arrays.compileElement(c, v1, read_index(type, indices));
-    count -= 2;
-
-    // note: GCC 4.1.1 here makes a prety interesting optimization
-    // where it duplicates the loop below based on c->arrays.indicesType
-
-    do {
-        v2 = fetch_vertex(c, read_index(type, indices));
-        const uint32_t cc = v0->flags & v1->flags & v2->flags;
-        if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-            c->prims.renderTriangle(c, v0, v1, v2);
-        vertex_t* & consumed = ((winding^=1) ? v1 : v0);
-        consumed->locked = 0;
-        consumed = v2;
-        count--;
-    } while (count);
-    v0->locked = v1->locked = 0;
-    v2->locked = 0;
-}
-
-void drawIndexedPrimitivesTriangleStrip(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices) {
-    drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 1);
-}
-
-void drawIndexedPrimitivesTriangleFan(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices) {
-    drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 2);
-}
-
-void drawIndexedPrimitivesTriangles(ogles_context_t* c,
-        GLsizei count, const GLvoid *indices)
-{
-    if (ggl_unlikely(count < 3))
-        return;
-
-    count -= 3;
-    if (ggl_likely(c->arrays.indicesType == GL_UNSIGNED_SHORT)) {
-        // This case is probably our most common case...
-        uint16_t const * p = (uint16_t const *)indices;
-        do {
-            vertex_t* const v0 = fetch_vertex(c, *p++);
-            vertex_t* const v1 = fetch_vertex(c, *p++);
-            vertex_t* const v2 = fetch_vertex(c, *p++);
-            const uint32_t cc = v0->flags & v1->flags & v2->flags;
-            if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-                c->prims.renderTriangle(c, v0, v1, v2);
-            v0->locked = 0;
-            v1->locked = 0;
-            v2->locked = 0;
-            count -= 3;
-        } while (count >= 0);
-    } else {
-        uint8_t const * p = (uint8_t const *)indices;
-        do {
-            vertex_t* const v0 = fetch_vertex(c, *p++);
-            vertex_t* const v1 = fetch_vertex(c, *p++);
-            vertex_t* const v2 = fetch_vertex(c, *p++);
-            const uint32_t cc = v0->flags & v1->flags & v2->flags;
-            if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
-                c->prims.renderTriangle(c, v0, v1, v2);
-            v0->locked = 0;
-            v1->locked = 0;
-            v2->locked = 0;
-            count -= 3;
-        } while (count >= 0);
-    }
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Array compilers
-#endif
-
-void compileElement__generic(ogles_context_t* c,
-        vertex_t* v, GLint first)
-{
-    v->flags = 0;
-    v->index = first;
-    first &= vertex_cache_t::INDEX_MASK;
-    const GLubyte* vp = c->arrays.vertex.element(first);
-    v->obj.z = 0;
-    v->obj.w = 0x10000;
-    c->arrays.vertex.fetch(c, v->obj.v, vp);
-    c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
-    c->arrays.perspective(c, v);
-}
-
-void compileElements__generic(ogles_context_t* c,
-        vertex_t* v, GLint first, GLsizei count)
-{
-    const GLubyte* vp = c->arrays.vertex.element(
-            first & vertex_cache_t::INDEX_MASK);
-    const size_t stride = c->arrays.vertex.stride;
-    transform_t const* const mvp = &c->transforms.mvp;
-    do {
-        v->flags = 0;
-        v->index = first++;
-        v->obj.z = 0;
-        v->obj.w = 0x10000;
-        c->arrays.vertex.fetch(c, v->obj.v, vp);
-        c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
-        c->arrays.perspective(c, v);
-        vp += stride;
-        v++;
-    } while (--count);
-}
-
-/*
-void compileElements__3x_full(ogles_context_t* c,
-        vertex_t* v, GLint first, GLsizei count)
-{
-    const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first);
-    const size_t stride = c->arrays.vertex.stride / 4;
-//    const GLfixed* const& m = c->transforms.mvp.matrix.m;
-
-    GLfixed m[16];
-    memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m));
-
-    do {
-        const GLfixed rx = vp[0];
-        const GLfixed ry = vp[1];
-        const GLfixed rz = vp[2];
-        vp += stride;
-        v->index = first++;
-        v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]);
-        v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
-        v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
-        v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]);
-
-        const GLfixed w = v->clip.w;
-        uint32_t clip = 0;
-        if (v->clip.x < -w)   clip |= vertex_t::CLIP_L;
-        if (v->clip.x >  w)   clip |= vertex_t::CLIP_R;
-        if (v->clip.y < -w)   clip |= vertex_t::CLIP_B;
-        if (v->clip.y >  w)   clip |= vertex_t::CLIP_T;
-        if (v->clip.z < -w)   clip |= vertex_t::CLIP_N;
-        if (v->clip.z >  w)   clip |= vertex_t::CLIP_F;
-        v->flags = clip;
-        c->arrays.cull &= clip;
-
-        //c->arrays.perspective(c, v);
-        v++;
-    } while (--count);
-}
-*/
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark clippers
-#endif
-
-static void clipVec4(vec4_t& nv,
-        GLfixed t, const vec4_t& s, const vec4_t& p)
-{
-    for (int i=0; i<4 ; i++)
-        nv.v[i] = gglMulAddx(t, s.v[i] - p.v[i], p.v[i], 28);
-}
-
-static void clipVertex(ogles_context_t* c, vertex_t* nv,
-        GLfixed t, const vertex_t* s, const vertex_t* p)
-{
-    clipVec4(nv->clip, t, s->clip, p->clip);
-    nv->fog = gglMulAddx(t, s->fog - p->fog, p->fog, 28);
-    ogles_vertex_project(c, nv);
-    nv->flags |=  vertex_t::LIT | vertex_t::EYE | vertex_t::TT;
-    nv->flags &= ~vertex_t::CLIP_ALL;
-}
-
-static void clipVertexC(ogles_context_t* c, vertex_t* nv,
-        GLfixed t, const vertex_t* s, const vertex_t* p)
-{
-    clipVec4(nv->color, t, s->color, p->color);
-    clipVertex(c, nv, t, s, p);
-}
-
-static void clipVertexT(ogles_context_t* c, vertex_t* nv,
-        GLfixed t, const vertex_t* s, const vertex_t* p)
-{
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        if (c->rasterizer.state.texture[i].enable)
-            clipVec4(nv->texture[i], t, s->texture[i], p->texture[i]);
-    }
-    clipVertex(c, nv, t, s, p);
-}
-
-static void clipVertexAll(ogles_context_t* c, vertex_t* nv,
-        GLfixed t, const vertex_t* s, const vertex_t* p)
-{
-    clipVec4(nv->color, t, s->color, p->color);
-    clipVertexT(c, nv, t, s, p);
-}
-
-static void clipEye(ogles_context_t* c, vertex_t* nv,
-        GLfixed t, const vertex_t* s, const vertex_t* p)
-{
-    nv->clear();
-    c->arrays.clipVertex(c, nv, t, p, s);
-    clipVec4(nv->eye, t, s->eye, p->eye);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-void validate_arrays(ogles_context_t* c, GLenum mode)
-{
-    uint32_t enables = c->rasterizer.state.enables;
-
-    // Perspective correction is not need if Ortho transform, but
-    // the user can still provide the w coordinate manually, so we can't
-    // automatically turn it off (in fact we could when the 4th coordinate
-    // is not spcified in the vertex array).
-    // W interpolation is never needed for points.
-    GLboolean perspective =
-        c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS);
-    c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective);
-
-    // set anti-aliasing
-    GLboolean smooth = GL_FALSE;
-    switch (mode) {
-    case GL_POINTS:
-        smooth = c->point.smooth;
-        break;
-    case GL_LINES:
-    case GL_LINE_LOOP:
-    case GL_LINE_STRIP:
-        smooth = c->line.smooth;
-        break;
-    }
-    if (((enables & GGL_ENABLE_AA)?1:0) != smooth)
-        c->rasterizer.procs.enableDisable(c, GGL_AA, smooth);
-
-    // set the shade model for this primitive
-    c->rasterizer.procs.shadeModel(c,
-            (mode == GL_POINTS) ? GL_FLAT : c->lighting.shadeModel);
-
-    // compute all the matrices we'll need...
-    uint32_t want =
-            transform_state_t::MVP |
-            transform_state_t::VIEWPORT;
-    if (c->lighting.enable) { // needs normal transforms and eye coords
-        want |= transform_state_t::MVUI;
-        want |= transform_state_t::MODELVIEW;
-    }
-    if (enables & GGL_ENABLE_TMUS) { // needs texture transforms
-        want |= transform_state_t::TEXTURE;
-    }
-    if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) {
-        want |= transform_state_t::MODELVIEW; // needs eye coords
-    }
-    ogles_validate_transform(c, want);
-
-    // textures...
-    if (enables & GGL_ENABLE_TMUS)
-        ogles_validate_texture(c);
-
-    // vertex compilers
-    c->arrays.compileElement = compileElement__generic;
-    c->arrays.compileElements = compileElements__generic;
-
-    // vertex transform
-    c->arrays.mvp_transform =
-        c->transforms.mvp.pointv[c->arrays.vertex.size - 2];
-
-    c->arrays.mv_transform =
-        c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2];
-
-    /*
-     * ***********************************************************************
-     *  pick fetchers
-     * ***********************************************************************
-     */
-
-    array_machine_t& am = c->arrays;
-    am.vertex.fetch = fetchNop;
-    am.normal.fetch = currentNormal;
-    am.color.fetch = currentColor;
-
-    if (am.vertex.enable) {
-        am.vertex.resolve();
-        if (am.vertex.bo || am.vertex.pointer) {
-            am.vertex.fetch = vertex_fct[am.vertex.size-2][am.vertex.type & 0xF];
-        }
-    }
-
-    if (am.normal.enable) {
-        am.normal.resolve();
-        if (am.normal.bo || am.normal.pointer) {
-            am.normal.fetch = normal_fct[am.normal.size-3][am.normal.type & 0xF];
-        }
-    }
-
-    if (am.color.enable) {
-        am.color.resolve();
-        if (c->lighting.enable) {
-            if (am.color.bo || am.color.pointer) {
-                am.color.fetch = color_fct[am.color.size-3][am.color.type & 0xF];
-            }
-        } else {
-            if (am.color.bo || am.color.pointer) {
-                am.color.fetch = color_clamp_fct[am.color.size-3][am.color.type & 0xF];
-            }
-        }
-    }
-
-    int activeTmuCount = 0;
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        am.texture[i].fetch = currentTexCoord;
-        if (c->rasterizer.state.texture[i].enable) {
-
-            // texture fetchers...
-            if (am.texture[i].enable) {
-                am.texture[i].resolve();
-                if (am.texture[i].bo || am.texture[i].pointer) {
-                    am.texture[i].fetch = texture_fct[am.texture[i].size-2][am.texture[i].type & 0xF];
-                }
-            }
-
-            // texture transform...
-            const int index = c->arrays.texture[i].size - 2;
-            c->arrays.tex_transform[i] =
-                c->transforms.texture[i].transform.pointv[index];
-
-            am.tmu = i;
-            activeTmuCount++;
-        }
-    }
-
-    // pick the vertex-clipper
-    uint32_t clipper = 0;
-    // we must reload 'enables' here
-    enables = c->rasterizer.state.enables;
-    if (enables & GGL_ENABLE_SMOOTH)
-        clipper |= 1;   // we need to interpolate colors
-    if (enables & GGL_ENABLE_TMUS)
-        clipper |= 2;   // we need to interpolate textures
-    switch (clipper) {
-    case 0: c->arrays.clipVertex = clipVertex;      break;
-    case 1: c->arrays.clipVertex = clipVertexC;     break;
-    case 2: c->arrays.clipVertex = clipVertexT;     break;
-    case 3: c->arrays.clipVertex = clipVertexAll;   break;
-    }
-    c->arrays.clipEye = clipEye;
-
-    // pick the primitive rasterizer
-    ogles_validate_primitives(c);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-#if 0
-#pragma mark -
-#pragma mark array API
-#endif
-
-void glVertexPointer(
-    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (size<2 || size>4 || stride<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    switch (type) {
-    case GL_BYTE:
-    case GL_SHORT:
-    case GL_FIXED:
-    case GL_FLOAT:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->arrays.vertex.init(size, type, stride, pointer, c->arrays.array_buffer, 0);
-}
-
-void glColorPointer(
-    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (size!=4 || stride<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    switch (type) {
-    case GL_UNSIGNED_BYTE:
-    case GL_FIXED:
-    case GL_FLOAT:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->arrays.color.init(size, type, stride, pointer, c->arrays.array_buffer, 0);
-}
-
-void glNormalPointer(
-    GLenum type, GLsizei stride, const GLvoid *pointer)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (stride<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    switch (type) {
-    case GL_BYTE:
-    case GL_SHORT:
-    case GL_FIXED:
-    case GL_FLOAT:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->arrays.normal.init(3, type, stride, pointer, c->arrays.array_buffer, 0);
-}
-
-void glTexCoordPointer(
-    GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (size<2 || size>4 || stride<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    switch (type) {
-    case GL_BYTE:
-    case GL_SHORT:
-    case GL_FIXED:
-    case GL_FLOAT:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    const int tmu = c->arrays.activeTexture;
-    c->arrays.texture[tmu].init(size, type, stride, pointer,
-            c->arrays.array_buffer, 0);
-}
-
-
-void glEnableClientState(GLenum array) {
-    ogles_context_t* c = ogles_context_t::get();
-    enableDisableClientState(c, array, true);
-}
-
-void glDisableClientState(GLenum array) {
-    ogles_context_t* c = ogles_context_t::get();
-    enableDisableClientState(c, array, false);
-}
-
-void glClientActiveTexture(GLenum texture)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (texture<GL_TEXTURE0 || texture>=GL_TEXTURE0+GGL_TEXTURE_UNIT_COUNT) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->arrays.activeTexture = texture - GL_TEXTURE0;
-}
-
-void glDrawArrays(GLenum mode, GLint first, GLsizei count)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (count<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    switch (mode) {
-    case GL_POINTS:
-    case GL_LINE_STRIP:
-    case GL_LINE_LOOP:
-    case GL_LINES:
-    case GL_TRIANGLE_STRIP:
-    case GL_TRIANGLE_FAN:
-    case GL_TRIANGLES:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    if (count == 0 || !c->arrays.vertex.enable)
-        return;
-    if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
-        return; // all triangles are culled
-
-
-    validate_arrays(c, mode);
-
-    const uint32_t enables = c->rasterizer.state.enables;
-    if (enables & GGL_ENABLE_TMUS)
-        ogles_lock_textures(c);
-
-    drawArraysPrims[mode](c, first, count);
-
-    if (enables & GGL_ENABLE_TMUS)
-        ogles_unlock_textures(c);
-
-#if VC_CACHE_STATISTICS
-    c->vc.total = count;
-    c->vc.dump_stats(mode);
-#endif
-}
-
-void glDrawElements(
-    GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (count<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    switch (mode) {
-    case GL_POINTS:
-    case GL_LINE_STRIP:
-    case GL_LINE_LOOP:
-    case GL_LINES:
-    case GL_TRIANGLE_STRIP:
-    case GL_TRIANGLE_FAN:
-    case GL_TRIANGLES:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    switch (type) {
-    case GL_UNSIGNED_BYTE:
-    case GL_UNSIGNED_SHORT:
-        c->arrays.indicesType = type;
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (count == 0 || !c->arrays.vertex.enable)
-        return;
-    if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
-        return; // all triangles are culled
-
-    // clear the vertex-cache
-    c->vc.clear();
-    validate_arrays(c, mode);
-
-    // if indices are in a buffer object, the pointer is treated as an
-    // offset in that buffer.
-    if (c->arrays.element_array_buffer) {
-        indices = c->arrays.element_array_buffer->data + uintptr_t(indices);
-    }
-
-    const uint32_t enables = c->rasterizer.state.enables;
-    if (enables & GGL_ENABLE_TMUS)
-        ogles_lock_textures(c);
-
-    drawElementsPrims[mode](c, count, indices);
-    
-    if (enables & GGL_ENABLE_TMUS)
-        ogles_unlock_textures(c);
-
-    
-#if VC_CACHE_STATISTICS
-    c->vc.total = count;
-    c->vc.dump_stats(mode);
-#endif
-}
-
-// ----------------------------------------------------------------------------
-// buffers
-// ----------------------------------------------------------------------------
-
-void glBindBuffer(GLenum target, GLuint buffer)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    // create a buffer object, or bind an existing one
-    buffer_t const* bo = 0;
-    if (buffer) {
-        bo = c->bufferObjectManager->bind(buffer);
-        if (!bo) {
-            ogles_error(c, GL_OUT_OF_MEMORY);
-            return;
-        }
-    }
-    ((target == GL_ARRAY_BUFFER) ?
-            c->arrays.array_buffer : c->arrays.element_array_buffer) = bo;
-}
-
-void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (size<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if ((usage!=GL_STATIC_DRAW) && (usage!=GL_DYNAMIC_DRAW)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
-            c->arrays.array_buffer : c->arrays.element_array_buffer);
-
-    if (bo == 0) {
-        // can't modify buffer 0
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-
-    buffer_t* edit_bo = const_cast<buffer_t*>(bo);
-    if (c->bufferObjectManager->allocateStore(edit_bo, size, usage) != 0) {
-        ogles_error(c, GL_OUT_OF_MEMORY);
-        return;
-    }
-    if (data) {
-        memcpy(bo->data, data, size);
-    }
-}
-
-void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (offset<0 || size<0 || data==0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
-            c->arrays.array_buffer : c->arrays.element_array_buffer);
-
-    if (bo == 0) {
-        // can't modify buffer 0
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-    if (offset+size > bo->size) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    memcpy(bo->data + offset, data, size);
-}
-
-void glDeleteBuffers(GLsizei n, const GLuint* buffers)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (n<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    for (int i=0 ; i<n ; i++) {
-        GLuint name = buffers[i];
-        if (name) {
-            // unbind bound deleted buffers...
-            if (c->arrays.element_array_buffer) {
-                if (c->arrays.element_array_buffer->name == name) {
-                    c->arrays.element_array_buffer = 0;
-                }
-            }
-            if (c->arrays.array_buffer) {
-                if (c->arrays.array_buffer->name == name) {
-                    c->arrays.array_buffer = 0;
-                }
-            }
-            if (c->arrays.vertex.bo) {
-                if (c->arrays.vertex.bo->name == name) {
-                    c->arrays.vertex.bo = 0;
-                }
-            }
-            if (c->arrays.normal.bo) {
-                if (c->arrays.normal.bo->name == name) {
-                    c->arrays.normal.bo = 0;
-                }
-            }
-            if (c->arrays.color.bo) {
-                if (c->arrays.color.bo->name == name) {
-                    c->arrays.color.bo = 0;
-                }
-            }
-            for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) {
-                if (c->arrays.texture[t].bo) {
-                    if (c->arrays.texture[t].bo->name == name) {
-                        c->arrays.texture[t].bo = 0;
-                    }
-                }
-            }
-        }
-    }
-    c->bufferObjectManager->deleteBuffers(n, buffers);
-    c->bufferObjectManager->recycleTokens(n, buffers);
-}
-
-void glGenBuffers(GLsizei n, GLuint* buffers)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (n<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    c->bufferObjectManager->getToken(n, buffers);
-}
diff --git a/opengl/libagl/array.h b/opengl/libagl/array.h
deleted file mode 100644
index e156978..0000000
--- a/opengl/libagl/array.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* libs/opengles/array.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_ARRAY_H
-#define ANDROID_OPENGLES_ARRAY_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-namespace android {
-
-namespace gl {
-struct ogles_context_t;
-};
-
-void ogles_init_array(ogles_context_t* c);
-void ogles_uninit_array(ogles_context_t* c);
-
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_ARRAY_H
-
diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h
deleted file mode 100644
index 6e77a23..0000000
--- a/opengl/libagl/context.h
+++ /dev/null
@@ -1,647 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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_OPENGLES_CONTEXT_H
-#define ANDROID_OPENGLES_CONTEXT_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-#include <pthread.h>
-#ifdef __ANDROID__
-#include <bionic/tls.h>
-#endif
-
-#include <private/pixelflinger/ggl_context.h>
-
-#include <system/window.h>
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-namespace android {
-
-
-const unsigned int OGLES_NUM_COMPRESSED_TEXTURE_FORMATS = 10
-#ifdef GL_OES_compressed_ETC1_RGB8_texture
-        + 1
-#endif
-        ;
-
-class EGLTextureObject;
-class EGLSurfaceManager;
-class EGLBufferObjectManager;
-
-namespace gl {
-
-struct ogles_context_t;
-struct matrixx_t;
-struct transform_t;
-struct buffer_t;
-
-ogles_context_t* getGlContext();
-
-template<typename T>
-static inline void swap(T& a, T& b) {
-    T t(a); a = b; b = t;
-}
-template<typename T>
-inline T max(T a, T b) {
-    return a<b ? b : a;
-}
-template<typename T>
-inline T max(T a, T b, T c) {
-    return max(a, max(b, c));
-}
-template<typename T>
-inline T min(T a, T b) {
-    return a<b ? a : b;
-}
-template<typename T>
-inline T min(T a, T b, T c) {
-    return min(a, min(b, c));
-}
-template<typename T>
-inline T min(T a, T b, T c, T d) {
-    return min(min(a,b), min(c,d));
-}
-
-// ----------------------------------------------------------------------------
-// vertices
-// ----------------------------------------------------------------------------
-
-struct vec3_t {
-    union {
-        struct { GLfixed x, y, z; };
-        struct { GLfixed r, g, b; };
-        struct { GLfixed S, T, R; };
-        GLfixed v[3];
-    };
-};
-
-struct vec4_t {
-    union {
-        struct { GLfixed x, y, z, w; };
-        struct { GLfixed r, g, b, a; };
-        struct { GLfixed S, T, R, Q; };
-        GLfixed v[4];
-    };
-};
-
-struct vertex_t {
-    enum {
-        // these constant matter for our clipping
-        CLIP_L          = 0x0001,   // clipping flags
-        CLIP_R          = 0x0002,
-        CLIP_B          = 0x0004,
-        CLIP_T          = 0x0008,
-        CLIP_N          = 0x0010,
-        CLIP_F          = 0x0020,
-
-        EYE             = 0x0040,
-        RESERVED        = 0x0080,
-
-        USER_CLIP_0     = 0x0100,   // user clipping flags
-        USER_CLIP_1     = 0x0200,
-        USER_CLIP_2     = 0x0400,
-        USER_CLIP_3     = 0x0800,
-        USER_CLIP_4     = 0x1000,
-        USER_CLIP_5     = 0x2000,
-
-        LIT             = 0x4000,   // lighting has been applied
-        TT              = 0x8000,   // texture coords transformed
-
-        FRUSTUM_CLIP_ALL= 0x003F,
-        USER_CLIP_ALL   = 0x3F00,
-        CLIP_ALL        = 0x3F3F,
-    };
-
-    // the fields below are arranged to minimize d-cache usage
-    // we group together, by cache-line, the fields most likely to be used
-
-    union {
-    vec4_t          obj;
-    vec4_t          eye;
-    };
-    vec4_t          clip;
-
-    uint32_t        flags;
-    size_t          index;  // cache tag, and vertex index
-    GLfixed         fog;
-    uint8_t         locked;
-    uint8_t         mru;
-    uint8_t         reserved[2];
-    vec4_t          window;
-
-    vec4_t          color;
-    vec4_t          texture[GGL_TEXTURE_UNIT_COUNT];
-#ifdef __LP64__
-    uint32_t        reserved1[2];
-#else
-    uint32_t        reserved1[4];
-#endif
-
-    inline void clear() {
-        flags = index = locked = mru = 0;
-    }
-};
-
-struct point_size_t {
-    GGLcoord    size;
-    GLboolean   smooth;
-};
-
-struct line_width_t {
-    GGLcoord    width;
-    GLboolean   smooth;
-};
-
-struct polygon_offset_t {
-    GLfixed     factor;
-    GLfixed     units;
-    GLboolean   enable;
-};
-
-// ----------------------------------------------------------------------------
-// arrays
-// ----------------------------------------------------------------------------
-
-struct array_t {
-    typedef void (*fetcher_t)(ogles_context_t*, GLfixed*, const GLvoid*);
-    fetcher_t       fetch;
-    GLvoid const*   physical_pointer;
-    GLint           size;
-    GLsizei         stride;
-    GLvoid const*   pointer;
-    buffer_t const* bo;
-    uint16_t        type;
-    GLboolean       enable;
-    GLboolean       pad;
-    GLsizei         bounds;
-    void init(GLint, GLenum, GLsizei, const GLvoid *, const buffer_t*, GLsizei);
-    inline void resolve();
-    inline const GLubyte* element(GLint i) const {
-        return (const GLubyte*)physical_pointer + i * stride;
-    }
-};
-
-struct array_machine_t {
-    array_t         vertex;
-    array_t         normal;
-    array_t         color;
-    array_t         texture[GGL_TEXTURE_UNIT_COUNT];
-    uint8_t         activeTexture;
-    uint8_t         tmu;
-    uint16_t        cull;
-    uint32_t        flags;
-    GLenum          indicesType;
-    buffer_t const* array_buffer;
-    buffer_t const* element_array_buffer;
-
-    void (*compileElements)(ogles_context_t*, vertex_t*, GLint, GLsizei);
-    void (*compileElement)(ogles_context_t*, vertex_t*, GLint);
-
-    void (*mvp_transform)(transform_t const*, vec4_t*, vec4_t const*);
-    void (*mv_transform)(transform_t const*, vec4_t*, vec4_t const*);
-    void (*tex_transform[2])(transform_t const*, vec4_t*, vec4_t const*);
-    void (*perspective)(ogles_context_t*c, vertex_t* v);
-    void (*clipVertex)(ogles_context_t* c, vertex_t* nv,
-            GGLfixed t, const vertex_t* s, const vertex_t* p);
-    void (*clipEye)(ogles_context_t* c, vertex_t* nv,
-            GGLfixed t, const vertex_t* s, const vertex_t* p);
-};
-
-struct vertex_cache_t {
-    enum {
-        // must be at least 4
-        // 3 vertice for triangles
-        // or 2 + 2 for indexed triangles w/ cache contention
-        VERTEX_BUFFER_SIZE  = 8,
-        // must be a power of two and at least 3
-        VERTEX_CACHE_SIZE   = 64,   // 8 KB
-
-        INDEX_BITS      = 16,
-        INDEX_MASK      = ((1LU<<INDEX_BITS)-1),
-        INDEX_SEQ       = 1LU<<INDEX_BITS,
-    };
-    vertex_t*       vBuffer;
-    vertex_t*       vCache;
-    uint32_t        sequence;
-    void*           base;
-    uint32_t        total;
-    uint32_t        misses;
-    int64_t         startTime;
-    void init();
-    void uninit();
-    void clear();
-    void dump_stats(GLenum mode);
-};
-
-// ----------------------------------------------------------------------------
-// fog
-// ----------------------------------------------------------------------------
-
-struct fog_t {
-    GLfixed     density;
-    GLfixed     start;
-    GLfixed     end;
-    GLfixed     invEndMinusStart;
-    GLenum      mode;
-    GLfixed     (*fog)(ogles_context_t* c, GLfixed z);
-};
-
-// ----------------------------------------------------------------------------
-// user clip planes
-// ----------------------------------------------------------------------------
-
-const unsigned int OGLES_MAX_CLIP_PLANES = 6;
-
-struct clip_plane_t {
-    vec4_t      equation;
-};
-
-struct user_clip_planes_t {
-    clip_plane_t    plane[OGLES_MAX_CLIP_PLANES];
-    uint32_t        enable;
-};
-
-// ----------------------------------------------------------------------------
-// lighting
-// ----------------------------------------------------------------------------
-
-const unsigned int OGLES_MAX_LIGHTS = 8;
-
-struct light_t {
-    vec4_t      ambient;
-    vec4_t      diffuse;
-    vec4_t      specular;
-    vec4_t      implicitAmbient;
-    vec4_t      implicitDiffuse;
-    vec4_t      implicitSpecular;
-    vec4_t      position;       // position in eye space
-    vec4_t      objPosition;
-    vec4_t      normalizedObjPosition;
-    vec4_t      spotDir;
-    vec4_t      normalizedSpotDir;
-    GLfixed     spotExp;
-    GLfixed     spotCutoff;
-    GLfixed     spotCutoffCosine;
-    GLfixed     attenuation[3];
-    GLfixed     rConstAttenuation;
-    GLboolean   enable;
-};
-
-struct material_t {
-    vec4_t      ambient;
-    vec4_t      diffuse;
-    vec4_t      specular;
-    vec4_t      emission;
-    GLfixed     shininess;
-};
-
-struct light_model_t {
-    vec4_t      ambient;
-    GLboolean   twoSide;
-};
-
-struct color_material_t {
-    GLenum      face;
-    GLenum      mode;
-    GLboolean   enable;
-};
-
-struct lighting_t {
-    light_t             lights[OGLES_MAX_LIGHTS];
-    material_t          front;
-    light_model_t       lightModel;
-    color_material_t    colorMaterial;
-    vec4_t              implicitSceneEmissionAndAmbient;
-    vec4_t              objViewer;
-    uint32_t            enabledLights;
-    GLboolean           enable;
-    GLenum              shadeModel;
-    typedef void (*light_fct_t)(ogles_context_t*, vertex_t*);
-    void (*lightVertex)(ogles_context_t* c, vertex_t* v);
-    void (*lightTriangle)(ogles_context_t* c,
-            vertex_t* v0, vertex_t* v1, vertex_t* v2);
-};
-
-struct culling_t {
-    GLenum      cullFace;
-    GLenum      frontFace;
-    GLboolean   enable;
-};
-
-// ----------------------------------------------------------------------------
-// textures
-// ----------------------------------------------------------------------------
-
-struct texture_unit_t {
-    GLuint              name;
-    EGLTextureObject*   texture;
-    uint8_t             dirty;
-};
-
-struct texture_state_t
-{
-    texture_unit_t      tmu[GGL_TEXTURE_UNIT_COUNT];
-    int                 active;     // active tmu
-    EGLTextureObject*   defaultTexture;
-    GGLContext*         ggl;
-    uint8_t             packAlignment;
-    uint8_t             unpackAlignment;
-};
-
-// ----------------------------------------------------------------------------
-// transformation and matrices
-// ----------------------------------------------------------------------------
-
-struct matrixf_t;
-
-struct matrixx_t {
-    GLfixed m[16];
-    void load(const matrixf_t& rhs);
-};
-
-struct matrix_stack_t;
-
-
-struct matrixf_t {
-    void loadIdentity();
-    void load(const matrixf_t& rhs);
-
-    inline GLfloat* editElements() { return m; }
-    inline GLfloat const* elements() const { return m; }
-
-    void set(const GLfixed* rhs);
-    void set(const GLfloat* rhs);
-
-    static void multiply(matrixf_t& r,
-            const matrixf_t& lhs, const matrixf_t& rhs);
-
-    void dump(const char* what);
-
-private:
-    friend struct matrix_stack_t;
-    GLfloat     m[16];
-    void load(const GLfixed* rhs);
-    void load(const GLfloat* rhs);
-    void multiply(const matrixf_t& rhs);
-    void translate(GLfloat x, GLfloat y, GLfloat z);
-    void scale(GLfloat x, GLfloat y, GLfloat z);
-    void rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z);
-};
-
-enum {
-    OP_IDENTITY         = 0x00,
-    OP_TRANSLATE        = 0x01,
-    OP_UNIFORM_SCALE    = 0x02,
-    OP_SCALE            = 0x05,
-    OP_ROTATE           = 0x08,
-    OP_SKEW             = 0x10,
-    OP_ALL              = 0x1F
-};
-
-struct transform_t {
-    enum {
-        FLAGS_2D_PROJECTION = 0x1
-    };
-    matrixx_t       matrix;
-    uint32_t        flags;
-    uint32_t        ops;
-
-    union {
-        struct {
-            void (*point2)(transform_t const* t, vec4_t*, vec4_t const*);
-            void (*point3)(transform_t const* t, vec4_t*, vec4_t const*);
-            void (*point4)(transform_t const* t, vec4_t*, vec4_t const*);
-        };
-        void (*pointv[3])(transform_t const* t, vec4_t*, vec4_t const*);
-    };
-
-    void loadIdentity();
-    void picker();
-    void dump(const char* what);
-};
-
-struct mvui_transform_t : public transform_t
-{
-    void picker();
-};
-
-struct matrix_stack_t {
-    enum {
-        DO_PICKER           = 0x1,
-        DO_FLOAT_TO_FIXED   = 0x2
-    };
-    transform_t     transform;
-    uint8_t         maxDepth;
-    uint8_t         depth;
-    uint8_t         dirty;
-    uint8_t         reserved;
-    matrixf_t       *stack;
-    uint8_t         *ops;
-    void init(int depth);
-    void uninit();
-    void loadIdentity();
-    void load(const GLfixed* rhs);
-    void load(const GLfloat* rhs);
-    void multiply(const matrixf_t& rhs);
-    void translate(GLfloat x, GLfloat y, GLfloat z);
-    void scale(GLfloat x, GLfloat y, GLfloat z);
-    void rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z);
-    GLint push();
-    GLint pop();
-    void validate();
-    matrixf_t& top() { return stack[depth]; }
-    const matrixf_t& top() const { return stack[depth]; }
-    uint32_t top_ops() const { return ops[depth]; }
-    inline bool isRigidBody() const {
-        return !(ops[depth] & ~(OP_TRANSLATE|OP_UNIFORM_SCALE|OP_ROTATE));
-    }
-};
-
-struct vp_transform_t {
-    transform_t     transform;
-    matrixf_t       matrix;
-    GLfloat         zNear;
-    GLfloat         zFar;
-    void loadIdentity();
-};
-
-struct transform_state_t {
-    enum {
-        MODELVIEW           = 0x01,
-        PROJECTION          = 0x02,
-        VIEWPORT            = 0x04,
-        TEXTURE             = 0x08,
-        MVUI                = 0x10,
-        MVIT                = 0x20,
-        MVP                 = 0x40,
-    };
-    matrix_stack_t      *current;
-    matrix_stack_t      modelview;
-    matrix_stack_t      projection;
-    matrix_stack_t      texture[GGL_TEXTURE_UNIT_COUNT];
-
-    // modelview * projection
-    transform_t         mvp     __attribute__((aligned(32)));
-    // viewport transformation
-    vp_transform_t      vpt     __attribute__((aligned(32)));
-    // same for 4-D vertices
-    transform_t         mvp4;
-    // full modelview inverse transpose
-    transform_t         mvit4;
-    // upper 3x3 of mv-inverse-transpose (for normals)
-    mvui_transform_t    mvui;
-
-    GLenum              matrixMode;
-    GLenum              rescaleNormals;
-    uint32_t            dirty;
-    void invalidate();
-    void update_mvp();
-    void update_mvit();
-    void update_mvui();
-};
-
-struct viewport_t {
-    GLint       x;
-    GLint       y;
-    GLsizei     w;
-    GLsizei     h;
-    struct {
-        GLint       x;
-        GLint       y;
-    } surfaceport;
-    struct {
-        GLint       x;
-        GLint       y;
-        GLsizei     w;
-        GLsizei     h;
-    } scissor;
-};
-
-// ----------------------------------------------------------------------------
-// Lerping
-// ----------------------------------------------------------------------------
-
-struct compute_iterators_t
-{
-    void initTriangle(
-            vertex_t const* v0,
-            vertex_t const* v1,
-            vertex_t const* v2);
-
-    void initLine(
-            vertex_t const* v0,
-            vertex_t const* v1);
-
-    inline void initLerp(vertex_t const* v0, uint32_t enables);
-
-    int iteratorsScale(int32_t it[3],
-            int32_t c0, int32_t c1, int32_t c2) const;
-
-    void iterators1616(GGLfixed it[3],
-            GGLfixed c0, GGLfixed c1, GGLfixed c2) const;
-
-    void iterators0032(int32_t it[3],
-            int32_t c0, int32_t c1, int32_t c2) const;
-
-    void iterators0032(int64_t it[3],
-            int32_t c0, int32_t c1, int32_t c2) const;
-
-    GGLcoord area() const { return m_area; }
-
-private:
-    // don't change order of members here -- used by iterators.S
-    GGLcoord m_dx01, m_dy10, m_dx20, m_dy02;
-    GGLcoord m_x0, m_y0;
-    GGLcoord m_area;
-    uint8_t m_scale;
-    uint8_t m_area_scale;
-    uint8_t m_reserved[2];
-
-};
-
-// ----------------------------------------------------------------------------
-// state
-// ----------------------------------------------------------------------------
-
-#ifdef __ANDROID__
-    // We have a dedicated TLS slot in bionic
-    inline void setGlThreadSpecific(ogles_context_t *value) {
-        __get_tls()[TLS_SLOT_OPENGL] = value;
-    }
-    inline ogles_context_t* getGlThreadSpecific() {
-        return static_cast<ogles_context_t*>(__get_tls()[TLS_SLOT_OPENGL]);
-    }
-#else
-    extern pthread_key_t gGLKey;
-    inline void setGlThreadSpecific(ogles_context_t *value) {
-        pthread_setspecific(gGLKey, value);
-    }
-    inline ogles_context_t* getGlThreadSpecific() {
-        return static_cast<ogles_context_t*>(pthread_getspecific(gGLKey));
-    }
-#endif
-
-
-struct prims_t {
-    typedef ogles_context_t* GL;
-    void (*renderPoint)(GL, vertex_t*);
-    void (*renderLine)(GL, vertex_t*, vertex_t*);
-    void (*renderTriangle)(GL, vertex_t*, vertex_t*, vertex_t*);
-};
-
-struct ogles_context_t {
-    context_t               rasterizer;
-    array_machine_t         arrays         __attribute__((aligned(32)));
-    texture_state_t         textures;
-    transform_state_t       transforms;
-    vertex_cache_t          vc;
-    prims_t                 prims;
-    culling_t               cull;
-    lighting_t              lighting;
-    user_clip_planes_t      clipPlanes;
-    compute_iterators_t     lerp           __attribute__((aligned(32)));
-    vertex_t                current;
-    vec4_t                  currentColorClamped;
-    vec3_t                  currentNormal;
-    viewport_t              viewport;
-    point_size_t            point;
-    line_width_t            line;
-    polygon_offset_t        polygonOffset;
-    fog_t                   fog;
-    uint32_t                perspective : 1;
-    uint32_t                transformTextures : 1;
-    EGLSurfaceManager*      surfaceManager;
-    EGLBufferObjectManager* bufferObjectManager;
-
-    GLenum                  error;
-
-    static inline ogles_context_t* get() {
-        return getGlThreadSpecific();
-    }
-
-};
-
-}; // namespace gl
-}; // namespace android
-
-using namespace android::gl;
-
-#endif // ANDROID_OPENGLES_CONTEXT_H
-
diff --git a/opengl/libagl/dxt.cpp b/opengl/libagl/dxt.cpp
deleted file mode 100644
index 238c81f..0000000
--- a/opengl/libagl/dxt.cpp
+++ /dev/null
@@ -1,636 +0,0 @@
-/* libs/opengles/dxt.cpp
-**
-** 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 TIMING 0
-
-#if TIMING
-#include <sys/time.h> // for optimization timing
-#include <stdio.h>
-#include <stdlib.h>
-#endif
-
-#include <GLES/gl.h>
-#include <utils/Endian.h>
-
-#include "context.h"
-
-#define TIMING 0
-
-namespace android {
-
-static uint8_t avg23tab[64*64];
-static volatile int tables_initialized = 0;
-
-// Definitions below are equivalent to these over the valid range of arguments
-//  #define div5(x) ((x)/5)
-//  #define div7(x) ((x)/7)
-
-// Use fixed-point to divide by 5 and 7
-// 3277 = 2^14/5 + 1
-// 2341 = 2^14/7 + 1
-#define div5(x) (((x)*3277) >> 14)
-#define div7(x) (((x)*2341) >> 14)
-
-// Table with entry [a << 6 | b] = (2*a + b)/3 for 0 <= a,b < 64
-#define avg23(x0,x1) avg23tab[((x0) << 6) | (x1)]
-
-// Extract 5/6/5 RGB
-#define red(x)   (((x) >> 11) & 0x1f)
-#define green(x) (((x) >>  5) & 0x3f)
-#define blue(x)  ( (x)        & 0x1f)
-
-/*
- * Convert 5/6/5 RGB (as 3 ints) to 8/8/8
- *
- * Operation count: 8 <<, 0 &, 5 |
- */
-inline static int rgb565SepTo888(int r, int g, int b)
-
-{
-    return ((((r << 3) | (r >> 2)) << 16) |
-            (((g << 2) | (g >> 4)) <<  8) |
-             ((b << 3) | (b >> 2)));
-}
-
-/*
- * Convert 5/6/5 RGB (as a single 16-bit word) to 8/8/8
- *
- *                   r4r3r2r1 r0g5g4g3 g2g1g0b4 b3b2b1b0   rgb
- *            r4r3r2 r1r0g5g4 g3g2g1g0 b4b3b2b1 b0 0 0 0   rgb << 3
- * r4r3r2r1 r0r4r3r2 g5g4g3g2 g1g0g5g4 b4b3b2b1 b0b4b3b2   desired result
- *
- * Construct the 24-bit RGB word as:
- *
- * r4r3r2r1 r0------ -------- -------- -------- --------  (rgb << 8) & 0xf80000
- *            r4r3r2 -------- -------- -------- --------  (rgb << 3) & 0x070000
- *                   g5g4g3g2 g1g0---- -------- --------  (rgb << 5) & 0x00fc00
- *                                g5g4 -------- --------  (rgb >> 1) & 0x000300
- *                                     b4b3b2b1 b0------  (rgb << 3) & 0x0000f8
- *                                                b4b3b2  (rgb >> 2) & 0x000007
- *
- * Operation count: 5 <<, 6 &, 5 | (n.b. rgb >> 3 is used twice)
- */
-inline static int rgb565To888(int rgb)
-
-{
-    int rgb3 = rgb >> 3;
-    return (((rgb << 8) & 0xf80000) |
-            ( rgb3      & 0x070000) |
-            ((rgb << 5) & 0x00fc00) |
-            ((rgb >> 1) & 0x000300) |
-            ( rgb3      & 0x0000f8) |
-            ((rgb >> 2) & 0x000007));
-}
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-static uint32_t swap(uint32_t x) {
-    int b0 = (x >> 24) & 0xff;
-    int b1 = (x >> 16) & 0xff;
-    int b2 = (x >>  8) & 0xff;
-    int b3 = (x      ) & 0xff;
-    
-    return (uint32_t)((b3 << 24) | (b2 << 16) | (b1 << 8) | b0);
-}
-#endif
-
-static void
-init_tables()
-{
-    if (tables_initialized) {
-        return;
-    }
-
-    for (int i = 0; i < 64; i++) {
-        for (int j = 0; j < 64; j++) {
-            int avg = (2*i + j)/3;
-            avg23tab[(i << 6) | j] = avg;
-        }
-    }
-
-    asm volatile ("" : : : "memory");
-    tables_initialized = 1;
-}
-
-/*
- * Utility to scan a DXT1 compressed texture to determine whether it
- * contains a transparent pixel (color0 < color1, code == 3).  This
- * may be useful if the application lacks information as to whether
- * the true format is GL_COMPRESSED_RGB_S3TC_DXT1_EXT or
- * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT.
- */
-bool
-DXT1HasAlpha(const GLvoid *data, int width, int height) {    
-#if TIMING
-    struct timeval start_t, end_t;
-    struct timezone tz;
-    
-    gettimeofday(&start_t, &tz);
-#endif
-
-    bool hasAlpha = false;
-
-    int xblocks = (width + 3)/4;
-    int yblocks = (height + 3)/4;
-    int numblocks = xblocks*yblocks;
-
-    uint32_t const *d32 = (uint32_t *)data;
-    for (int b = 0; b < numblocks; b++) {
-        uint32_t colors = *d32++;
-        
-#if __BYTE_ORDER == __BIG_ENDIAN
-        colors = swap(colors);
-#endif
-        
-        uint16_t color0 = colors & 0xffff;
-        uint16_t color1 = colors >> 16;
-        
-        if (color0 < color1) {
-            // There's no need to endian-swap within 'bits'
-            // since we don't care which pixel is the transparent one
-            uint32_t bits = *d32++;
-            
-            // Detect if any (odd, even) pair of bits are '11'
-            //      bits: b31 b30 b29 ... b3 b2 b1 b0
-            // bits >> 1: b31 b31 b30 ... b4 b3 b2 b1
-            //         &: b31 (b31 & b30) (b29 & b28) ... (b2 & b1) (b1 & b0)
-            //  & 0x55..:   0 (b31 & b30)       0     ...     0     (b1 & b0)
-            if (((bits & (bits >> 1)) & 0x55555555) != 0) {
-                hasAlpha = true;
-                goto done;
-            }
-        } else {
-            // Skip 4 bytes
-            ++d32;
-        }
-    }
-    
- done:
-#if TIMING
-    gettimeofday(&end_t, &tz);
-    long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 +
-        (end_t.tv_usec - start_t.tv_usec);
-    
-    printf("Scanned w=%d h=%d in %ld usec\n", width, height, usec);
-#endif
-    
-    return hasAlpha;
-}
-
-static void
-decodeDXT1(const GLvoid *data, int width, int height,
-           void *surface, int stride,
-           bool hasAlpha)
-    
-{
-    init_tables();
-    
-    uint32_t const *d32 = (uint32_t *)data;
-    
-    // Color table for the current block
-    uint16_t c[4];
-    c[0] = c[1] = c[2] = c[3] = 0;
-    
-    // Specified colors from the previous block
-    uint16_t prev_color0 = 0x0000;
-    uint16_t prev_color1 = 0x0000;
-    
-    uint16_t* rowPtr = (uint16_t*)surface;
-    for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) {
-        uint16_t *blockPtr = rowPtr;
-        for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) {
-            uint32_t colors = *d32++;
-            uint32_t bits = *d32++;
-            
-#if __BYTE_ORDER == __BIG_ENDIAN
-            colors = swap(colors);
-            bits = swap(bits);
-#endif
-            
-            // Raw colors
-            uint16_t color0 = colors & 0xffff;
-            uint16_t color1 = colors >> 16;
-            
-            // If the new block has the same base colors as the
-            // previous one, we don't need to recompute the color
-            // table c[]
-            if (color0 != prev_color0 || color1 != prev_color1) {
-                // Store raw colors for comparison with next block
-                prev_color0 = color0;
-                prev_color1 = color1;
-                
-                int r0 =   red(color0);
-                int g0 = green(color0);
-                int b0 =  blue(color0);
-
-                int r1 =   red(color1);
-                int g1 = green(color1);
-                int b1 =  blue(color1);                
-                
-                if (hasAlpha) {
-                    c[0] = (r0 << 11) | ((g0 >> 1) << 6) | (b0 << 1) | 0x1;
-                    c[1] = (r1 << 11) | ((g1 >> 1) << 6) | (b1 << 1) | 0x1;
-                } else {
-                    c[0] = color0;
-                    c[1] = color1;
-                }
-                
-                int r2, g2, b2, r3, g3, b3, a3;
-                
-                int bbits = bits >> 1;
-                bool has2 = ((bbits & ~bits) & 0x55555555) != 0;
-                bool has3 = ((bbits &  bits) & 0x55555555) != 0;
-                
-                if (has2 || has3) {
-                    if (color0 > color1) {
-                        r2 = avg23(r0, r1);
-                        g2 = avg23(g0, g1);
-                        b2 = avg23(b0, b1);
-                        
-                        r3 = avg23(r1, r0);
-                        g3 = avg23(g1, g0);
-                        b3 = avg23(b1, b0);
-                        a3 = 1;
-                    } else {
-                        r2 = (r0 + r1) >> 1;
-                        g2 = (g0 + g1) >> 1;
-                        b2 = (b0 + b1) >> 1;
-                        
-                        r3 = g3 = b3 = a3 = 0;
-                    }
-                    if (hasAlpha) {
-                        c[2] = (r2 << 11) | ((g2 >> 1) << 6) |
-                            (b2 << 1) | 0x1;
-                        c[3] = (r3 << 11) | ((g3 >> 1) << 6) |
-                            (b3 << 1) | a3;
-                    } else {
-                        c[2] = (r2 << 11) | (g2 << 5) | b2;
-                        c[3] = (r3 << 11) | (g3 << 5) | b3;
-                    }
-                }
-            }
-            
-            uint16_t* blockRowPtr = blockPtr;
-            for (int y = 0; y < 4; y++, blockRowPtr += stride) {
-                // Don't process rows past the botom
-                if (base_y + y >= height) {
-                    break;
-                }
-                
-                int w = min(width - base_x, 4);
-                for (int x = 0; x < w; x++) {
-                    int code = bits & 0x3;
-                    bits >>= 2;
-                    
-                    blockRowPtr[x] = c[code];
-                }
-            }
-        }
-    }
-}
-    
-// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE
-static void
-decodeDXT3(const GLvoid *data, int width, int height,
-           void *surface, int stride)
-
-{
-    init_tables();
-    
-    uint32_t const *d32 = (uint32_t *)data;
-    
-    // Specified colors from the previous block
-    uint16_t prev_color0 = 0x0000;
-    uint16_t prev_color1 = 0x0000;
-
-    // Color table for the current block
-    uint32_t c[4];
-    c[0] = c[1] = c[2] = c[3] = 0;
-
-    uint32_t* rowPtr = (uint32_t*)surface;
-    for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) {
-        uint32_t *blockPtr = rowPtr;
-        for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) {
-            
-#if __BYTE_ORDER == __BIG_ENDIAN
-            uint32_t alphahi = *d32++;
-            uint32_t alphalo = *d32++;
-            alphahi = swap(alphahi);
-            alphalo = swap(alphalo);
-#else
-            uint32_t alphalo = *d32++;
-            uint32_t alphahi = *d32++;
-#endif
-
-            uint32_t colors = *d32++;
-            uint32_t bits = *d32++;
-            
-#if __BYTE_ORDER == __BIG_ENDIAN
-            colors = swap(colors);
-            bits = swap(bits);
-#endif
-            
-            uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo;
-
-            // Raw colors
-            uint16_t color0 = colors & 0xffff;
-            uint16_t color1 = colors >> 16;
-
-            // If the new block has the same base colors as the
-            // previous one, we don't need to recompute the color
-            // table c[]
-            if (color0 != prev_color0 || color1 != prev_color1) {
-                // Store raw colors for comparison with next block
-                prev_color0 = color0;
-                prev_color1 = color1;
-                
-                int bbits = bits >> 1;
-                bool has2 = ((bbits & ~bits) & 0x55555555) != 0;
-                bool has3 = ((bbits &  bits) & 0x55555555) != 0;
-                
-                if (has2 || has3) {
-                    int r0 =   red(color0);
-                    int g0 = green(color0);
-                    int b0 =  blue(color0);
-                    
-                    int r1 =   red(color1);
-                    int g1 = green(color1);
-                    int b1 =  blue(color1);
-                    
-                    int r2 = avg23(r0, r1);
-                    int g2 = avg23(g0, g1);
-                    int b2 = avg23(b0, b1);
-                    
-                    int r3 = avg23(r1, r0);
-                    int g3 = avg23(g1, g0);
-                    int b3 = avg23(b1, b0);
-
-                    c[0] = rgb565SepTo888(r0, g0, b0);
-                    c[1] = rgb565SepTo888(r1, g1, b1);
-                    c[2] = rgb565SepTo888(r2, g2, b2);
-                    c[3] = rgb565SepTo888(r3, g3, b3);
-                } else {
-                    // Convert to 8 bits
-                    c[0] = rgb565To888(color0);
-                    c[1] = rgb565To888(color1);
-                }
-            }
-
-            uint32_t* blockRowPtr = blockPtr;
-            for (int y = 0; y < 4; y++, blockRowPtr += stride) {
-                // Don't process rows past the botom
-                if (base_y + y >= height) {
-                    break;
-                }
-                
-                int w = min(width - base_x, 4);
-                for (int x = 0; x < w; x++) {
-                    int a = alpha & 0xf;
-                    alpha >>= 4;
-
-                    int code = bits & 0x3;
-                    bits >>= 2;
-
-                    blockRowPtr[x] = c[code] | (a << 28) | (a << 24);
-                }
-            }
-        }
-    }
-}
-
-// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE
-static void
-decodeDXT5(const GLvoid *data, int width, int height,
-           void *surface, int stride)
-
-{
-    init_tables();
-    
-    uint32_t const *d32 = (uint32_t *)data;
-    
-    // Specified alphas from the previous block
-    uint8_t prev_alpha0 = 0x00;
-    uint8_t prev_alpha1 = 0x00;
-
-    // Specified colors from the previous block
-    uint16_t prev_color0 = 0x0000;
-     uint16_t prev_color1 = 0x0000;
-
-    // Alpha table for the current block
-    uint8_t a[8];
-    a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = 0;
-
-    // Color table for the current block
-    uint32_t c[4];
-    c[0] = c[1] = c[2] = c[3] = 0;
-
-    int good_a5 = 0;
-    int bad_a5 = 0;
-    int good_a6 = 0;
-    int bad_a6 = 0;
-    int good_a7 = 0;
-    int bad_a7 = 0;
-
-    uint32_t* rowPtr = (uint32_t*)surface;
-    for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) {
-        uint32_t *blockPtr = rowPtr;
-        for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) {
-            
-#if __BYTE_ORDER == __BIG_ENDIAN
-            uint32_t alphahi = *d32++;
-            uint32_t alphalo = *d32++;
-            alphahi = swap(alphahi);
-            alphalo = swap(alphalo);
-#else
-             uint32_t alphalo = *d32++;
-             uint32_t alphahi = *d32++;
-#endif
-
-            uint32_t colors = *d32++;
-            uint32_t bits = *d32++;
-            
-#if __BYTE_ORDER == __BIG_ENDIANx
-            colors = swap(colors);
-            bits = swap(bits);
-#endif
-            
-            uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo;
-            uint64_t alpha0 = alpha & 0xff;
-            alpha >>= 8;
-            uint64_t alpha1 = alpha & 0xff;
-            alpha >>= 8;
-
-            if (alpha0 != prev_alpha0 || alpha1 != prev_alpha1) {
-                prev_alpha0 = alpha0;
-                prev_alpha1 = alpha1;
-                
-                a[0] = alpha0;
-                a[1] = alpha1;
-                int a01 = alpha0 + alpha1 - 1;
-                if (alpha0 > alpha1) {
-                    a[2] = div7(6*alpha0 +   alpha1);
-                    a[4] = div7(4*alpha0 + 3*alpha1);
-                    a[6] = div7(2*alpha0 + 5*alpha1);
-
-                    // Use symmetry to derive half of the values
-                    // A few values will be off by 1 (~.5%)
-                    // Alternate which values are computed directly
-                    // and which are derived to try to reduce bias
-                    a[3] = a01 - a[6];
-                    a[5] = a01 - a[4];
-                    a[7] = a01 - a[2];
-                } else {
-                    a[2] = div5(4*alpha0 +   alpha1);
-                    a[4] = div5(2*alpha0 + 3*alpha1);
-                    a[3] = a01 - a[4];
-                    a[5] = a01 - a[2];
-                    a[6] = 0x00;
-                    a[7] = 0xff;
-                }
-            }
-
-            // Raw colors
-            uint16_t color0 = colors & 0xffff;
-            uint16_t color1 = colors >> 16;
-
-            // If the new block has the same base colors as the
-            // previous one, we don't need to recompute the color
-            // table c[]
-            if (color0 != prev_color0 || color1 != prev_color1) {
-                // Store raw colors for comparison with next block
-                prev_color0 = color0;
-                prev_color1 = color1;
-                
-                int bbits = bits >> 1;
-                bool has2 = ((bbits & ~bits) & 0x55555555) != 0;
-                bool has3 = ((bbits &  bits) & 0x55555555) != 0;
-                
-                if (has2 || has3) {
-                    int r0 =   red(color0);
-                    int g0 = green(color0);
-                    int b0 =  blue(color0);
-                    
-                    int r1 =   red(color1);
-                    int g1 = green(color1);
-                    int b1 =  blue(color1);
-                
-                    int r2 = avg23(r0, r1);
-                    int g2 = avg23(g0, g1);
-                    int b2 = avg23(b0, b1);
-                    
-                    int r3 = avg23(r1, r0);
-                    int g3 = avg23(g1, g0);
-                    int b3 = avg23(b1, b0);
-
-                    c[0] = rgb565SepTo888(r0, g0, b0);
-                    c[1] = rgb565SepTo888(r1, g1, b1);
-                    c[2] = rgb565SepTo888(r2, g2, b2);
-                    c[3] = rgb565SepTo888(r3, g3, b3);
-                } else {
-                    // Convert to 8 bits
-                    c[0] = rgb565To888(color0);
-                    c[1] = rgb565To888(color1);
-                }                
-            }
-
-            uint32_t* blockRowPtr = blockPtr;
-            for (int y = 0; y < 4; y++, blockRowPtr += stride) {
-                // Don't process rows past the botom
-                if (base_y + y >= height) {
-                    break;
-                }
-                
-                int w = min(width - base_x, 4);
-                for (int x = 0; x < w; x++) {
-                    int acode = alpha & 0x7;
-                    alpha >>= 3;
-
-                    int code = bits & 0x3;
-                    bits >>= 2;
-
-                    blockRowPtr[x] = c[code] | (a[acode] << 24);
-                }
-            }
-        }
-    }
-}
-   
-/*
- * Decode a DXT-compressed texture into memory.  DXT textures consist of
- * a series of 4x4 pixel blocks in left-to-right, top-down order.
- * The number of blocks is given by ceil(width/4)*ceil(height/4).
- *
- * 'data' points to the texture data. 'width' and 'height' indicate the
- * dimensions of the texture.  We assume width and height are >= 0 but
- * do not require them to be powers of 2 or divisible by any factor.
- *
- * The output is written to 'surface' with each scanline separated by
- * 'stride' 2- or 4-byte words.
- *
- * 'format' indicates the type of compression and must be one of the following:
- *
- *   GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
- *      The output is written as 5/6/5 opaque RGB (16 bit words).
- *      8 bytes are read from 'data' for each block.
- *
- *   GL_COMPRESSED_RGBA_S3TC_DXT1_EXT
- *      The output is written as 5/5/5/1 RGBA (16 bit words)
- *      8 bytes are read from 'data' for each block.
- *
- *   GL_COMPRESSED_RGBA_S3TC_DXT3_EXT
- *   GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
- *      The output is written as 8/8/8/8 ARGB (32 bit words)
- *      16 bytes are read from 'data' for each block.
- */
-void
-decodeDXT(const GLvoid *data, int width, int height,
-          void *surface, int stride, int format)
-{
-#if TIMING
-    struct timeval start_t, end_t;
-    struct timezone tz;
-    
-    gettimeofday(&start_t, &tz);
-#endif
-
-    switch (format) {
-    case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-        decodeDXT1(data, width, height, surface, stride, false);
-        break;
-        
-    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-        decodeDXT1(data, width, height, surface, stride, true);
-        break;
-        
-    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
-        decodeDXT3(data, width, height, surface, stride);
-        break;
-        
-    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
-        decodeDXT5(data, width, height, surface, stride);
-        break;
-    }
-    
-#if TIMING
-    gettimeofday(&end_t, &tz);
-    long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 +
-        (end_t.tv_usec - start_t.tv_usec);
-    
-    printf("Loaded w=%d h=%d in %ld usec\n", width, height, usec);
-#endif
-}
-
-} // namespace android
diff --git a/opengl/libagl/dxt.h b/opengl/libagl/dxt.h
deleted file mode 100644
index d95a36c..0000000
--- a/opengl/libagl/dxt.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* libs/opengles/dxt.h
-**
-** 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.
-*/
-
-#ifndef ANDROID_OPENGLES_TEXTURE_H
-#define ANDROID_OPENGLES_TEXTURE_H
-
-#include <stdlib.h>
-
-#include <GLES/gl.h>
-
-namespace android {
-
-  bool DXT1HasAlpha(const GLvoid *data, int width, int height);
-  void decodeDXT(const GLvoid *data, int width, int height,
-                 void *surface, int stride, int format);
-
-} // namespace android
-
-#endif // ANDROID_OPENGLES_TEXTURE_H
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
deleted file mode 100644
index be43705..0000000
--- a/opengl/libagl/egl.cpp
+++ /dev/null
@@ -1,2230 +0,0 @@
-/*
-**
-** 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.
-*/
-
-#include <assert.h>
-#include <atomic>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include <utils/threads.h>
-#include <ui/ANativeObjectBase.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBufferMapper.h>
-#include <ui/Rect.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-#include <pixelflinger/format.h>
-#include <pixelflinger/pixelflinger.h>
-
-#include "context.h"
-#include "state.h"
-#include "texture.h"
-#include "matrix.h"
-
-#undef NELEM
-#define NELEM(x) (sizeof(x)/sizeof(*(x)))
-
-// ----------------------------------------------------------------------------
-
-EGLBoolean EGLAPI eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
-        EGLint left, EGLint top, EGLint width, EGLint height);
-
-
-typedef struct egl_native_pixmap_t
-{
-    int32_t     version;    /* must be 32 */
-    int32_t     width;
-    int32_t     height;
-    int32_t     stride;
-    uint8_t*    data;
-    uint8_t     format;
-    uint8_t     rfu[3];
-    union {
-        uint32_t    compressedFormat;
-        int32_t     vstride;
-    };
-    int32_t     reserved;
-} egl_native_pixmap_t;
-
-
-// ----------------------------------------------------------------------------
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-const unsigned int NUM_DISPLAYS = 1;
-
-#ifndef __ANDROID__
-static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER;
-#endif
-static pthread_mutex_t gErrorKeyMutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_key_t gEGLErrorKey = -1;
-#ifndef __ANDROID__
-namespace gl {
-pthread_key_t gGLKey = -1;
-}; // namespace gl
-#endif
-
-template<typename T>
-static T setError(GLint error, T returnValue) {
-    if (ggl_unlikely(gEGLErrorKey == -1)) {
-        pthread_mutex_lock(&gErrorKeyMutex);
-        if (gEGLErrorKey == -1)
-            pthread_key_create(&gEGLErrorKey, NULL);
-        pthread_mutex_unlock(&gErrorKeyMutex);
-    }
-    pthread_setspecific(gEGLErrorKey, (void*)(uintptr_t)error);
-    return returnValue;
-}
-
-static GLint getError() {
-    if (ggl_unlikely(gEGLErrorKey == -1))
-        return EGL_SUCCESS;
-    GLint error = (GLint)(uintptr_t)pthread_getspecific(gEGLErrorKey);
-    if (error == 0) {
-        // The TLS key has been created by another thread, but the value for
-        // this thread has not been initialized.
-        return EGL_SUCCESS;
-    }
-    pthread_setspecific(gEGLErrorKey, (void*)(uintptr_t)EGL_SUCCESS);
-    return error;
-}
-
-// ----------------------------------------------------------------------------
-
-struct egl_display_t
-{
-    egl_display_t() : type(0), initialized(0) { }
-
-    static egl_display_t& get_display(EGLDisplay dpy);
-
-    static EGLBoolean is_valid(EGLDisplay dpy) {
-        return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE;
-    }
-
-    NativeDisplayType  type;
-    std::atomic_size_t initialized;
-};
-
-static egl_display_t gDisplays[NUM_DISPLAYS];
-
-egl_display_t& egl_display_t::get_display(EGLDisplay dpy) {
-    return gDisplays[uintptr_t(dpy)-1U];
-}
-
-struct egl_context_t {
-    enum {
-        IS_CURRENT      =   0x00010000,
-        NEVER_CURRENT   =   0x00020000
-    };
-    uint32_t            flags;
-    EGLDisplay          dpy;
-    EGLConfig           config;
-    EGLSurface          read;
-    EGLSurface          draw;
-
-    static inline egl_context_t* context(EGLContext ctx) {
-        ogles_context_t* const gl = static_cast<ogles_context_t*>(ctx);
-        return static_cast<egl_context_t*>(gl->rasterizer.base);
-    }
-};
-
-// ----------------------------------------------------------------------------
-
-struct egl_surface_t
-{
-    enum {
-        PAGE_FLIP = 0x00000001,
-        MAGIC     = 0x31415265
-    };
-
-    uint32_t            magic;
-    EGLDisplay          dpy;
-    EGLConfig           config;
-    EGLContext          ctx;
-    bool                zombie;
-
-                egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat);
-    virtual     ~egl_surface_t();
-                bool    isValid() const;
-    virtual     bool    initCheck() const = 0;
-
-    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl) = 0;
-    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl) = 0;
-    virtual     EGLBoolean  connect() { return EGL_TRUE; }
-    virtual     void        disconnect() {}
-    virtual     EGLint      getWidth() const = 0;
-    virtual     EGLint      getHeight() const = 0;
-
-    virtual     EGLint      getHorizontalResolution() const;
-    virtual     EGLint      getVerticalResolution() const;
-    virtual     EGLint      getRefreshRate() const;
-    virtual     EGLint      getSwapBehavior() const;
-    virtual     EGLBoolean  swapBuffers();
-    virtual     EGLBoolean  setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
-protected:
-    GGLSurface              depth;
-};
-
-egl_surface_t::egl_surface_t(EGLDisplay dpy,
-        EGLConfig config,
-        int32_t depthFormat)
-    : magic(MAGIC), dpy(dpy), config(config), ctx(0), zombie(false)
-{
-    depth.version = sizeof(GGLSurface);
-    depth.data = 0;
-    depth.format = depthFormat;
-}
-egl_surface_t::~egl_surface_t()
-{
-    magic = 0;
-    free(depth.data);
-}
-bool egl_surface_t::isValid() const {
-    ALOGE_IF(magic != MAGIC, "invalid EGLSurface (%p)", this);
-    return magic == MAGIC; 
-}
-
-EGLBoolean egl_surface_t::swapBuffers() {
-    return EGL_FALSE;
-}
-EGLint egl_surface_t::getHorizontalResolution() const {
-    return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
-}
-EGLint egl_surface_t::getVerticalResolution() const {
-    return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
-}
-EGLint egl_surface_t::getRefreshRate() const {
-    return (60 * EGL_DISPLAY_SCALING);
-}
-EGLint egl_surface_t::getSwapBehavior() const {
-    return EGL_BUFFER_PRESERVED;
-}
-EGLBoolean egl_surface_t::setSwapRectangle(
-        EGLint /*l*/, EGLint /*t*/, EGLint /*w*/, EGLint /*h*/)
-{
-    return EGL_FALSE;
-}
-
-// ----------------------------------------------------------------------------
-
-struct egl_window_surface_v2_t : public egl_surface_t
-{
-    egl_window_surface_v2_t(
-            EGLDisplay dpy, EGLConfig config,
-            int32_t depthFormat,
-            ANativeWindow* window);
-
-    ~egl_window_surface_v2_t();
-
-    virtual     bool        initCheck() const { return true; } // TODO: report failure if ctor fails
-    virtual     EGLBoolean  swapBuffers();
-    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
-    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
-    virtual     EGLBoolean  connect();
-    virtual     void        disconnect();
-    virtual     EGLint      getWidth() const    { return width;  }
-    virtual     EGLint      getHeight() const   { return height; }
-    virtual     EGLint      getHorizontalResolution() const;
-    virtual     EGLint      getVerticalResolution() const;
-    virtual     EGLint      getRefreshRate() const;
-    virtual     EGLint      getSwapBehavior() const;
-    virtual     EGLBoolean  setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
-    
-private:
-    status_t lock(ANativeWindowBuffer* buf, int usage, void** vaddr);
-    status_t unlock(ANativeWindowBuffer* buf);
-    ANativeWindow*   nativeWindow;
-    ANativeWindowBuffer*   buffer;
-    ANativeWindowBuffer*   previousBuffer;
-    int width;
-    int height;
-    void* bits;
-    GGLFormat const* pixelFormatTable;
-    
-    struct Rect {
-        inline Rect() { };
-        inline Rect(int32_t w, int32_t h)
-            : left(0), top(0), right(w), bottom(h) { }
-        inline Rect(int32_t l, int32_t t, int32_t r, int32_t b)
-            : left(l), top(t), right(r), bottom(b) { }
-        Rect& andSelf(const Rect& r) {
-            left   = max(left, r.left);
-            top    = max(top, r.top);
-            right  = min(right, r.right);
-            bottom = min(bottom, r.bottom);
-            return *this;
-        }
-        bool isEmpty() const {
-            return (left>=right || top>=bottom);
-        }
-        void dump(char const* what) {
-            ALOGD("%s { %5d, %5d, w=%5d, h=%5d }",
-                    what, left, top, right-left, bottom-top);
-        }
-        
-        int32_t left;
-        int32_t top;
-        int32_t right;
-        int32_t bottom;
-    };
-
-    struct Region {
-        inline Region() : count(0) { }
-        typedef Rect const* const_iterator;
-        const_iterator begin() const { return storage; }
-        const_iterator end() const { return storage+count; }
-        static Region subtract(const Rect& lhs, const Rect& rhs) {
-            Region reg;
-            Rect* storage = reg.storage;
-            if (!lhs.isEmpty()) {
-                if (lhs.top < rhs.top) { // top rect
-                    storage->left   = lhs.left;
-                    storage->top    = lhs.top;
-                    storage->right  = lhs.right;
-                    storage->bottom = rhs.top;
-                    storage++;
-                }
-                const int32_t top = max(lhs.top, rhs.top);
-                const int32_t bot = min(lhs.bottom, rhs.bottom);
-                if (top < bot) {
-                    if (lhs.left < rhs.left) { // left-side rect
-                        storage->left   = lhs.left;
-                        storage->top    = top;
-                        storage->right  = rhs.left;
-                        storage->bottom = bot;
-                        storage++;
-                    }
-                    if (lhs.right > rhs.right) { // right-side rect
-                        storage->left   = rhs.right;
-                        storage->top    = top;
-                        storage->right  = lhs.right;
-                        storage->bottom = bot;
-                        storage++;
-                    }
-                }
-                if (lhs.bottom > rhs.bottom) { // bottom rect
-                    storage->left   = lhs.left;
-                    storage->top    = rhs.bottom;
-                    storage->right  = lhs.right;
-                    storage->bottom = lhs.bottom;
-                    storage++;
-                }
-                reg.count = storage - reg.storage;
-            }
-            return reg;
-        }
-        bool isEmpty() const {
-            return count<=0;
-        }
-    private:
-        Rect storage[4];
-        ssize_t count;
-    };
-    
-    void copyBlt(
-            ANativeWindowBuffer* dst, void* dst_vaddr,
-            ANativeWindowBuffer* src, void const* src_vaddr,
-            const Region& clip);
-
-    Rect dirtyRegion;
-    Rect oldDirtyRegion;
-};
-
-egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
-        EGLConfig config,
-        int32_t depthFormat,
-        ANativeWindow* window)
-    : egl_surface_t(dpy, config, depthFormat),
-    nativeWindow(window), buffer(0), previousBuffer(0), bits(NULL)
-{
-
-    pixelFormatTable = gglGetPixelFormatTable();
-
-    // keep a reference on the window
-    nativeWindow->common.incRef(&nativeWindow->common);
-    nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width);
-    nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &height);
-}
-
-egl_window_surface_v2_t::~egl_window_surface_v2_t() {
-    if (buffer) {
-        buffer->common.decRef(&buffer->common);
-    }
-    if (previousBuffer) {
-        previousBuffer->common.decRef(&previousBuffer->common); 
-    }
-    nativeWindow->common.decRef(&nativeWindow->common);
-}
-
-EGLBoolean egl_window_surface_v2_t::connect() 
-{
-    // we're intending to do software rendering
-    native_window_set_usage(nativeWindow, 
-            GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
-
-    // dequeue a buffer
-    int fenceFd = -1;
-    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer,
-            &fenceFd) != NO_ERROR) {
-        return setError(EGL_BAD_ALLOC, EGL_FALSE);
-    }
-
-    // wait for the buffer
-    sp<Fence> fence(new Fence(fenceFd));
-    if (fence->wait(Fence::TIMEOUT_NEVER) != NO_ERROR) {
-        nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd);
-        return setError(EGL_BAD_ALLOC, EGL_FALSE);
-    }
-
-    // allocate a corresponding depth-buffer
-    width = buffer->width;
-    height = buffer->height;
-    if (depth.format) {
-        depth.width   = width;
-        depth.height  = height;
-        depth.stride  = depth.width; // use the width here
-        uint64_t allocSize = static_cast<uint64_t>(depth.stride) *
-                static_cast<uint64_t>(depth.height) * 2;
-        if (depth.stride < 0 || depth.height > INT_MAX ||
-                allocSize > UINT32_MAX) {
-            return setError(EGL_BAD_ALLOC, EGL_FALSE);
-        }
-        depth.data    = (GGLubyte*)malloc(allocSize);
-        if (depth.data == 0) {
-            return setError(EGL_BAD_ALLOC, EGL_FALSE);
-        }
-    }
-
-    // keep a reference on the buffer
-    buffer->common.incRef(&buffer->common);
-
-    // pin the buffer down
-    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | 
-            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
-        ALOGE("connect() failed to lock buffer %p (%ux%u)",
-                buffer, buffer->width, buffer->height);
-        return setError(EGL_BAD_ACCESS, EGL_FALSE);
-        // FIXME: we should make sure we're not accessing the buffer anymore
-    }
-    return EGL_TRUE;
-}
-
-void egl_window_surface_v2_t::disconnect() 
-{
-    if (buffer && bits) {
-        bits = NULL;
-        unlock(buffer);
-    }
-    if (buffer) {
-        nativeWindow->cancelBuffer(nativeWindow, buffer, -1);
-        buffer->common.decRef(&buffer->common);
-        buffer = 0;
-    }
-    if (previousBuffer) {
-        previousBuffer->common.decRef(&previousBuffer->common); 
-        previousBuffer = 0;
-    }
-}
-
-status_t egl_window_surface_v2_t::lock(
-        ANativeWindowBuffer* buf, int usage, void** vaddr)
-{
-    auto& mapper = GraphicBufferMapper::get();
-    return mapper.lock(buf->handle, usage,
-            android::Rect(buf->width, buf->height), vaddr);
-}
-
-status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf)
-{
-    if (!buf) return BAD_VALUE;
-    auto& mapper = GraphicBufferMapper::get();
-    return mapper.unlock(buf->handle);
-}
-
-void egl_window_surface_v2_t::copyBlt(
-        ANativeWindowBuffer* dst, void* dst_vaddr,
-        ANativeWindowBuffer* src, void const* src_vaddr,
-        const Region& clip)
-{
-    // NOTE: dst and src must be the same format
-    
-    Region::const_iterator cur = clip.begin();
-    Region::const_iterator end = clip.end();
-
-    const size_t bpp = pixelFormatTable[src->format].size;
-    const size_t dbpr = dst->stride * bpp;
-    const size_t sbpr = src->stride * bpp;
-
-    uint8_t const * const src_bits = (uint8_t const *)src_vaddr;
-    uint8_t       * const dst_bits = (uint8_t       *)dst_vaddr;
-
-    while (cur != end) {
-        const Rect& r(*cur++);
-        ssize_t w = r.right - r.left;
-        ssize_t h = r.bottom - r.top;
-        if (w <= 0 || h<=0) continue;
-        size_t size = w * bpp;
-        uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
-        uint8_t       * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
-        if (dbpr==sbpr && size==sbpr) {
-            size *= h;
-            h = 1;
-        }
-        do {
-            memcpy(d, s, size);
-            d += dbpr;
-            s += sbpr;
-        } while (--h > 0);
-    }
-}
-
-EGLBoolean egl_window_surface_v2_t::swapBuffers()
-{
-    if (!buffer) {
-        return setError(EGL_BAD_ACCESS, EGL_FALSE);
-    }
-    
-    /*
-     * Handle eglSetSwapRectangleANDROID()
-     * We copyback from the front buffer 
-     */
-    if (!dirtyRegion.isEmpty()) {
-        dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
-        if (previousBuffer) {
-            // This was const Region copyBack, but that causes an
-            // internal compile error on simulator builds
-            /*const*/ Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
-            if (!copyBack.isEmpty()) {
-                void* prevBits;
-                if (lock(previousBuffer, 
-                        GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
-                    // copy from previousBuffer to buffer
-                    copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
-                    unlock(previousBuffer);
-                }
-            }
-        }
-        oldDirtyRegion = dirtyRegion;
-    }
-
-    if (previousBuffer) {
-        previousBuffer->common.decRef(&previousBuffer->common); 
-        previousBuffer = 0;
-    }
-    
-    unlock(buffer);
-    previousBuffer = buffer;
-    nativeWindow->queueBuffer(nativeWindow, buffer, -1);
-    buffer = 0;
-
-    // dequeue a new buffer
-    int fenceFd = -1;
-    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR) {
-        sp<Fence> fence(new Fence(fenceFd));
-        if (fence->wait(Fence::TIMEOUT_NEVER)) {
-            nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd);
-            return setError(EGL_BAD_ALLOC, EGL_FALSE);
-        }
-
-        // reallocate the depth-buffer if needed
-        if ((width != buffer->width) || (height != buffer->height)) {
-            // TODO: we probably should reset the swap rect here
-            // if the window size has changed
-            width = buffer->width;
-            height = buffer->height;
-            if (depth.data) {
-                free(depth.data);
-                depth.width   = width;
-                depth.height  = height;
-                depth.stride  = buffer->stride;
-                uint64_t allocSize = static_cast<uint64_t>(depth.stride) *
-                        static_cast<uint64_t>(depth.height) * 2;
-                if (depth.stride < 0 || depth.height > INT_MAX ||
-                        allocSize > UINT32_MAX) {
-                    setError(EGL_BAD_ALLOC, EGL_FALSE);
-                    return EGL_FALSE;
-                }
-                depth.data    = (GGLubyte*)malloc(allocSize);
-                if (depth.data == 0) {
-                    setError(EGL_BAD_ALLOC, EGL_FALSE);
-                    return EGL_FALSE;
-                }
-            }
-        }
-
-        // keep a reference on the buffer
-        buffer->common.incRef(&buffer->common);
-
-        // finally pin the buffer down
-        if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
-                GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
-            ALOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
-                    buffer, buffer->width, buffer->height);
-            return setError(EGL_BAD_ACCESS, EGL_FALSE);
-            // FIXME: we should make sure we're not accessing the buffer anymore
-        }
-    } else {
-        return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
-    }
-
-    return EGL_TRUE;
-}
-
-EGLBoolean egl_window_surface_v2_t::setSwapRectangle(
-        EGLint l, EGLint t, EGLint w, EGLint h)
-{
-    dirtyRegion = Rect(l, t, l+w, t+h);
-    return EGL_TRUE;
-}
-
-EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl)
-{
-    GGLSurface buffer;
-    buffer.version = sizeof(GGLSurface);
-    buffer.width   = this->buffer->width;
-    buffer.height  = this->buffer->height;
-    buffer.stride  = this->buffer->stride;
-    buffer.data    = (GGLubyte*)bits;
-    buffer.format  = this->buffer->format;
-    gl->rasterizer.procs.colorBuffer(gl, &buffer);
-    if (depth.data != gl->rasterizer.state.buffers.depth.data)
-        gl->rasterizer.procs.depthBuffer(gl, &depth);
-
-    return EGL_TRUE;
-}
-EGLBoolean egl_window_surface_v2_t::bindReadSurface(ogles_context_t* gl)
-{
-    GGLSurface buffer;
-    buffer.version = sizeof(GGLSurface);
-    buffer.width   = this->buffer->width;
-    buffer.height  = this->buffer->height;
-    buffer.stride  = this->buffer->stride;
-    buffer.data    = (GGLubyte*)bits; // FIXME: hopefully is is LOCKED!!!
-    buffer.format  = this->buffer->format;
-    gl->rasterizer.procs.readBuffer(gl, &buffer);
-    return EGL_TRUE;
-}
-EGLint egl_window_surface_v2_t::getHorizontalResolution() const {
-    return (nativeWindow->xdpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
-}
-EGLint egl_window_surface_v2_t::getVerticalResolution() const {
-    return (nativeWindow->ydpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
-}
-EGLint egl_window_surface_v2_t::getRefreshRate() const {
-    return (60 * EGL_DISPLAY_SCALING); // FIXME
-}
-EGLint egl_window_surface_v2_t::getSwapBehavior() const 
-{
-    /*
-     * EGL_BUFFER_PRESERVED means that eglSwapBuffers() completely preserves
-     * the content of the swapped buffer.
-     * 
-     * EGL_BUFFER_DESTROYED means that the content of the buffer is lost.
-     * 
-     * However when ANDROID_swap_retcangle is supported, EGL_BUFFER_DESTROYED
-     * only applies to the area specified by eglSetSwapRectangleANDROID(), that
-     * is, everything outside of this area is preserved.
-     * 
-     * This implementation of EGL assumes the later case.
-     * 
-     */
-
-    return EGL_BUFFER_DESTROYED;
-}
-
-// ----------------------------------------------------------------------------
-
-struct egl_pixmap_surface_t : public egl_surface_t
-{
-    egl_pixmap_surface_t(
-            EGLDisplay dpy, EGLConfig config,
-            int32_t depthFormat,
-            egl_native_pixmap_t const * pixmap);
-
-    virtual ~egl_pixmap_surface_t() { }
-
-    virtual     bool        initCheck() const { return !depth.format || depth.data!=0; } 
-    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
-    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
-    virtual     EGLint      getWidth() const    { return nativePixmap.width;  }
-    virtual     EGLint      getHeight() const   { return nativePixmap.height; }
-private:
-    egl_native_pixmap_t     nativePixmap;
-};
-
-egl_pixmap_surface_t::egl_pixmap_surface_t(EGLDisplay dpy,
-        EGLConfig config,
-        int32_t depthFormat,
-        egl_native_pixmap_t const * pixmap)
-    : egl_surface_t(dpy, config, depthFormat), nativePixmap(*pixmap)
-{
-    if (depthFormat) {
-        depth.width   = pixmap->width;
-        depth.height  = pixmap->height;
-        depth.stride  = depth.width; // use the width here
-        uint64_t allocSize = static_cast<uint64_t>(depth.stride) *
-                static_cast<uint64_t>(depth.height) * 2;
-        if (depth.stride < 0 || depth.height > INT_MAX ||
-                allocSize > UINT32_MAX) {
-            setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
-            return;
-        }
-        depth.data    = (GGLubyte*)malloc(allocSize);
-        if (depth.data == 0) {
-            setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
-        }
-    }
-}
-EGLBoolean egl_pixmap_surface_t::bindDrawSurface(ogles_context_t* gl)
-{
-    GGLSurface buffer;
-    buffer.version = sizeof(GGLSurface);
-    buffer.width   = nativePixmap.width;
-    buffer.height  = nativePixmap.height;
-    buffer.stride  = nativePixmap.stride;
-    buffer.data    = nativePixmap.data;
-    buffer.format  = nativePixmap.format;
-
-    gl->rasterizer.procs.colorBuffer(gl, &buffer);
-    if (depth.data != gl->rasterizer.state.buffers.depth.data)
-        gl->rasterizer.procs.depthBuffer(gl, &depth);
-    return EGL_TRUE;
-}
-EGLBoolean egl_pixmap_surface_t::bindReadSurface(ogles_context_t* gl)
-{
-    GGLSurface buffer;
-    buffer.version = sizeof(GGLSurface);
-    buffer.width   = nativePixmap.width;
-    buffer.height  = nativePixmap.height;
-    buffer.stride  = nativePixmap.stride;
-    buffer.data    = nativePixmap.data;
-    buffer.format  = nativePixmap.format;
-    gl->rasterizer.procs.readBuffer(gl, &buffer);
-    return EGL_TRUE;
-}
-
-// ----------------------------------------------------------------------------
-
-struct egl_pbuffer_surface_t : public egl_surface_t
-{
-    egl_pbuffer_surface_t(
-            EGLDisplay dpy, EGLConfig config, int32_t depthFormat,
-            int32_t w, int32_t h, int32_t f);
-
-    virtual ~egl_pbuffer_surface_t();
-
-    virtual     bool        initCheck() const   { return pbuffer.data != 0; }
-    virtual     EGLBoolean  bindDrawSurface(ogles_context_t* gl);
-    virtual     EGLBoolean  bindReadSurface(ogles_context_t* gl);
-    virtual     EGLint      getWidth() const    { return pbuffer.width;  }
-    virtual     EGLint      getHeight() const   { return pbuffer.height; }
-private:
-    GGLSurface  pbuffer;
-};
-
-egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy,
-        EGLConfig config, int32_t depthFormat,
-        int32_t w, int32_t h, int32_t f)
-    : egl_surface_t(dpy, config, depthFormat)
-{
-    size_t size = w*h;
-    switch (f) {
-        case GGL_PIXEL_FORMAT_A_8:          size *= 1; break;
-        case GGL_PIXEL_FORMAT_RGB_565:      size *= 2; break;
-        case GGL_PIXEL_FORMAT_RGBA_8888:    size *= 4; break;
-        case GGL_PIXEL_FORMAT_RGBX_8888:    size *= 4; break;
-        case GGL_PIXEL_FORMAT_BGRA_8888:    size *= 4; break;
-        default:
-            ALOGE("incompatible pixel format for pbuffer (format=%d)", f);
-            pbuffer.data = 0;
-            break;
-    }
-    pbuffer.version = sizeof(GGLSurface);
-    pbuffer.width   = w;
-    pbuffer.height  = h;
-    pbuffer.stride  = w;
-    pbuffer.data    = (GGLubyte*)malloc(size);
-    pbuffer.format  = f;
-
-    if (depthFormat) {
-        depth.width   = pbuffer.width;
-        depth.height  = pbuffer.height;
-        depth.stride  = depth.width; // use the width here
-        uint64_t allocSize = static_cast<uint64_t>(depth.stride) *
-                static_cast<uint64_t>(depth.height) * 2;
-        if (depth.stride < 0 || depth.height > INT_MAX ||
-                allocSize > UINT32_MAX) {
-            setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
-            return;
-        }
-        depth.data    = (GGLubyte*)malloc(allocSize);
-        if (depth.data == 0) {
-            setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
-            return;
-        }
-    }
-}
-egl_pbuffer_surface_t::~egl_pbuffer_surface_t() {
-    free(pbuffer.data);
-}
-EGLBoolean egl_pbuffer_surface_t::bindDrawSurface(ogles_context_t* gl)
-{
-    gl->rasterizer.procs.colorBuffer(gl, &pbuffer);
-    if (depth.data != gl->rasterizer.state.buffers.depth.data)
-        gl->rasterizer.procs.depthBuffer(gl, &depth);
-    return EGL_TRUE;
-}
-EGLBoolean egl_pbuffer_surface_t::bindReadSurface(ogles_context_t* gl)
-{
-    gl->rasterizer.procs.readBuffer(gl, &pbuffer);
-    return EGL_TRUE;
-}
-
-// ----------------------------------------------------------------------------
-
-struct config_pair_t {
-    GLint key;
-    GLint value;
-};
-
-struct configs_t {
-    const config_pair_t* array;
-    int                  size;
-};
-
-struct config_management_t {
-    GLint key;
-    bool (*match)(GLint reqValue, GLint confValue);
-    static bool atLeast(GLint reqValue, GLint confValue) {
-        return (reqValue == EGL_DONT_CARE) || (confValue >= reqValue);
-    }
-    static bool exact(GLint reqValue, GLint confValue) {
-        return (reqValue == EGL_DONT_CARE) || (confValue == reqValue);
-    }
-    static bool mask(GLint reqValue, GLint confValue) {
-        return (confValue & reqValue) == reqValue;
-    }
-    static bool ignore(GLint /*reqValue*/, GLint /*confValue*/) {
-        return true;
-    }
-};
-
-// ----------------------------------------------------------------------------
-
-#define VERSION_MAJOR 1
-#define VERSION_MINOR 2
-static char const * const gVendorString     = "Google Inc.";
-static char const * const gVersionString    = "1.2 Android Driver 1.2.0";
-static char const * const gClientApiString  = "OpenGL_ES";
-static char const * const gExtensionsString =
-        "EGL_KHR_fence_sync "
-        "EGL_KHR_image_base "
-        // "KHR_image_pixmap "
-        "EGL_ANDROID_image_native_buffer "
-        "EGL_ANDROID_swap_rectangle "
-        ;
-
-// ----------------------------------------------------------------------------
-
-struct extention_map_t {
-    const char * const name;
-    __eglMustCastToProperFunctionPointerType address;
-};
-
-static const extention_map_t gExtentionMap[] = {
-    { "glDrawTexsOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexsOES },
-    { "glDrawTexiOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexiOES },
-    { "glDrawTexfOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexfOES },
-    { "glDrawTexxOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexxOES },
-    { "glDrawTexsvOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexsvOES },
-    { "glDrawTexivOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexivOES },
-    { "glDrawTexfvOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexfvOES },
-    { "glDrawTexxvOES",
-            (__eglMustCastToProperFunctionPointerType)&glDrawTexxvOES },
-    { "glQueryMatrixxOES",
-            (__eglMustCastToProperFunctionPointerType)&glQueryMatrixxOES },
-    { "glEGLImageTargetTexture2DOES",
-            (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetTexture2DOES },
-    { "glEGLImageTargetRenderbufferStorageOES",
-            (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetRenderbufferStorageOES },
-    { "glClipPlanef",
-            (__eglMustCastToProperFunctionPointerType)&glClipPlanef },
-    { "glClipPlanex",
-            (__eglMustCastToProperFunctionPointerType)&glClipPlanex },
-    { "glBindBuffer",
-            (__eglMustCastToProperFunctionPointerType)&glBindBuffer },
-    { "glBufferData",
-            (__eglMustCastToProperFunctionPointerType)&glBufferData },
-    { "glBufferSubData",
-            (__eglMustCastToProperFunctionPointerType)&glBufferSubData },
-    { "glDeleteBuffers",
-            (__eglMustCastToProperFunctionPointerType)&glDeleteBuffers },
-    { "glGenBuffers",
-            (__eglMustCastToProperFunctionPointerType)&glGenBuffers },
-    { "eglCreateImageKHR",  
-            (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, 
-    { "eglDestroyImageKHR", 
-            (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, 
-    { "eglCreateSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR },
-    { "eglDestroySyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR },
-    { "eglClientWaitSyncKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR },
-    { "eglGetSyncAttribKHR",
-            (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR },
-    { "eglSetSwapRectangleANDROID", 
-            (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, 
-};
-
-/*
- * In the lists below, attributes names MUST be sorted.
- * Additionally, all configs must be sorted according to
- * the EGL specification.
- */
-
-static config_pair_t const config_base_attribute_list[] = {
-        { EGL_STENCIL_SIZE,               0                                 },
-        { EGL_CONFIG_CAVEAT,              EGL_SLOW_CONFIG                   },
-        { EGL_LEVEL,                      0                                 },
-        { EGL_MAX_PBUFFER_HEIGHT,         GGL_MAX_VIEWPORT_DIMS             },
-        { EGL_MAX_PBUFFER_PIXELS,
-                GGL_MAX_VIEWPORT_DIMS*GGL_MAX_VIEWPORT_DIMS                 },
-        { EGL_MAX_PBUFFER_WIDTH,          GGL_MAX_VIEWPORT_DIMS             },
-        { EGL_NATIVE_RENDERABLE,          EGL_TRUE                          },
-        { EGL_NATIVE_VISUAL_ID,           0                                 },
-        { EGL_NATIVE_VISUAL_TYPE,         GGL_PIXEL_FORMAT_RGB_565          },
-        { EGL_SAMPLES,                    0                                 },
-        { EGL_SAMPLE_BUFFERS,             0                                 },
-        { EGL_TRANSPARENT_TYPE,           EGL_NONE                          },
-        { EGL_TRANSPARENT_BLUE_VALUE,     0                                 },
-        { EGL_TRANSPARENT_GREEN_VALUE,    0                                 },
-        { EGL_TRANSPARENT_RED_VALUE,      0                                 },
-        { EGL_BIND_TO_TEXTURE_RGBA,       EGL_FALSE                         },
-        { EGL_BIND_TO_TEXTURE_RGB,        EGL_FALSE                         },
-        { EGL_MIN_SWAP_INTERVAL,          1                                 },
-        { EGL_MAX_SWAP_INTERVAL,          1                                 },
-        { EGL_LUMINANCE_SIZE,             0                                 },
-        { EGL_ALPHA_MASK_SIZE,            0                                 },
-        { EGL_COLOR_BUFFER_TYPE,          EGL_RGB_BUFFER                    },
-        { EGL_RENDERABLE_TYPE,            EGL_OPENGL_ES_BIT                 },
-        { EGL_CONFORMANT,                 0                                 }
-};
-
-// These configs can override the base attribute list
-// NOTE: when adding a config here, don't forget to update eglCreate*Surface()
-
-// 565 configs
-static config_pair_t const config_0_attribute_list[] = {
-        { EGL_BUFFER_SIZE,     16 },
-        { EGL_ALPHA_SIZE,       0 },
-        { EGL_BLUE_SIZE,        5 },
-        { EGL_GREEN_SIZE,       6 },
-        { EGL_RED_SIZE,         5 },
-        { EGL_DEPTH_SIZE,       0 },
-        { EGL_CONFIG_ID,        0 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-static config_pair_t const config_1_attribute_list[] = {
-        { EGL_BUFFER_SIZE,     16 },
-        { EGL_ALPHA_SIZE,       0 },
-        { EGL_BLUE_SIZE,        5 },
-        { EGL_GREEN_SIZE,       6 },
-        { EGL_RED_SIZE,         5 },
-        { EGL_DEPTH_SIZE,      16 },
-        { EGL_CONFIG_ID,        1 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-// RGB 888 configs
-static config_pair_t const config_2_attribute_list[] = {
-        { EGL_BUFFER_SIZE,     32 },
-        { EGL_ALPHA_SIZE,       0 },
-        { EGL_BLUE_SIZE,        8 },
-        { EGL_GREEN_SIZE,       8 },
-        { EGL_RED_SIZE,         8 },
-        { EGL_DEPTH_SIZE,       0 },
-        { EGL_CONFIG_ID,        6 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-static config_pair_t const config_3_attribute_list[] = {
-        { EGL_BUFFER_SIZE,     32 },
-        { EGL_ALPHA_SIZE,       0 },
-        { EGL_BLUE_SIZE,        8 },
-        { EGL_GREEN_SIZE,       8 },
-        { EGL_RED_SIZE,         8 },
-        { EGL_DEPTH_SIZE,      16 },
-        { EGL_CONFIG_ID,        7 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-// 8888 configs
-static config_pair_t const config_4_attribute_list[] = {
-        { EGL_BUFFER_SIZE,     32 },
-        { EGL_ALPHA_SIZE,       8 },
-        { EGL_BLUE_SIZE,        8 },
-        { EGL_GREEN_SIZE,       8 },
-        { EGL_RED_SIZE,         8 },
-        { EGL_DEPTH_SIZE,       0 },
-        { EGL_CONFIG_ID,        2 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-static config_pair_t const config_5_attribute_list[] = {
-        { EGL_BUFFER_SIZE,     32 },
-        { EGL_ALPHA_SIZE,       8 },
-        { EGL_BLUE_SIZE,        8 },
-        { EGL_GREEN_SIZE,       8 },
-        { EGL_RED_SIZE,         8 },
-        { EGL_DEPTH_SIZE,      16 },
-        { EGL_CONFIG_ID,        3 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-// A8 configs
-static config_pair_t const config_6_attribute_list[] = {
-        { EGL_BUFFER_SIZE,      8 },
-        { EGL_ALPHA_SIZE,       8 },
-        { EGL_BLUE_SIZE,        0 },
-        { EGL_GREEN_SIZE,       0 },
-        { EGL_RED_SIZE,         0 },
-        { EGL_DEPTH_SIZE,       0 },
-        { EGL_CONFIG_ID,        4 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-static config_pair_t const config_7_attribute_list[] = {
-        { EGL_BUFFER_SIZE,      8 },
-        { EGL_ALPHA_SIZE,       8 },
-        { EGL_BLUE_SIZE,        0 },
-        { EGL_GREEN_SIZE,       0 },
-        { EGL_RED_SIZE,         0 },
-        { EGL_DEPTH_SIZE,      16 },
-        { EGL_CONFIG_ID,        5 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-// BGRA 8888 config
-static config_pair_t const config_8_attribute_list[] = {
-        { EGL_BUFFER_SIZE,     32 },
-        { EGL_ALPHA_SIZE,       8 },
-        { EGL_BLUE_SIZE,        8 },
-        { EGL_GREEN_SIZE,       8 },
-        { EGL_RED_SIZE,         8 },
-        { EGL_DEPTH_SIZE,       0 },
-        { EGL_CONFIG_ID,        8 },
-        { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_BGRA_8888 },
-        { EGL_SURFACE_TYPE,     EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT },
-};
-
-static configs_t const gConfigs[] = {
-        { config_0_attribute_list, NELEM(config_0_attribute_list) },
-        { config_1_attribute_list, NELEM(config_1_attribute_list) },
-        { config_2_attribute_list, NELEM(config_2_attribute_list) },
-        { config_3_attribute_list, NELEM(config_3_attribute_list) },
-        { config_4_attribute_list, NELEM(config_4_attribute_list) },
-        { config_5_attribute_list, NELEM(config_5_attribute_list) },
-        { config_6_attribute_list, NELEM(config_6_attribute_list) },
-        { config_7_attribute_list, NELEM(config_7_attribute_list) },
-        { config_8_attribute_list, NELEM(config_8_attribute_list) },
-};
-
-static config_management_t const gConfigManagement[] = {
-        { EGL_BUFFER_SIZE,                config_management_t::atLeast },
-        { EGL_ALPHA_SIZE,                 config_management_t::atLeast },
-        { EGL_BLUE_SIZE,                  config_management_t::atLeast },
-        { EGL_GREEN_SIZE,                 config_management_t::atLeast },
-        { EGL_RED_SIZE,                   config_management_t::atLeast },
-        { EGL_DEPTH_SIZE,                 config_management_t::atLeast },
-        { EGL_STENCIL_SIZE,               config_management_t::atLeast },
-        { EGL_CONFIG_CAVEAT,              config_management_t::exact   },
-        { EGL_CONFIG_ID,                  config_management_t::exact   },
-        { EGL_LEVEL,                      config_management_t::exact   },
-        { EGL_MAX_PBUFFER_HEIGHT,         config_management_t::ignore   },
-        { EGL_MAX_PBUFFER_PIXELS,         config_management_t::ignore   },
-        { EGL_MAX_PBUFFER_WIDTH,          config_management_t::ignore   },
-        { EGL_NATIVE_RENDERABLE,          config_management_t::exact   },
-        { EGL_NATIVE_VISUAL_ID,           config_management_t::ignore   },
-        { EGL_NATIVE_VISUAL_TYPE,         config_management_t::exact   },
-        { EGL_SAMPLES,                    config_management_t::exact   },
-        { EGL_SAMPLE_BUFFERS,             config_management_t::exact   },
-        { EGL_SURFACE_TYPE,               config_management_t::mask    },
-        { EGL_TRANSPARENT_TYPE,           config_management_t::exact   },
-        { EGL_TRANSPARENT_BLUE_VALUE,     config_management_t::exact   },
-        { EGL_TRANSPARENT_GREEN_VALUE,    config_management_t::exact   },
-        { EGL_TRANSPARENT_RED_VALUE,      config_management_t::exact   },
-        { EGL_BIND_TO_TEXTURE_RGBA,       config_management_t::exact   },
-        { EGL_BIND_TO_TEXTURE_RGB,        config_management_t::exact   },
-        { EGL_MIN_SWAP_INTERVAL,          config_management_t::exact   },
-        { EGL_MAX_SWAP_INTERVAL,          config_management_t::exact   },
-        { EGL_LUMINANCE_SIZE,             config_management_t::atLeast },
-        { EGL_ALPHA_MASK_SIZE,            config_management_t::atLeast },
-        { EGL_COLOR_BUFFER_TYPE,          config_management_t::exact   },
-        { EGL_RENDERABLE_TYPE,            config_management_t::mask    },
-        { EGL_CONFORMANT,                 config_management_t::mask    }
-};
-
-
-static config_pair_t const config_defaults[] = {
-    // attributes that are not specified are simply ignored, if a particular
-    // one needs not be ignored, it must be specified here, eg:
-    // { EGL_SURFACE_TYPE, EGL_WINDOW_BIT },
-};
-
-// ----------------------------------------------------------------------------
-
-static status_t getConfigFormatInfo(EGLint configID,
-        int32_t& pixelFormat, int32_t& depthFormat)
-{
-    switch(configID) {
-    case 0:
-        pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
-        depthFormat = 0;
-        break;
-    case 1:
-        pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
-        depthFormat = GGL_PIXEL_FORMAT_Z_16;
-        break;
-    case 2:
-        pixelFormat = GGL_PIXEL_FORMAT_RGBX_8888;
-        depthFormat = 0;
-        break;
-    case 3:
-        pixelFormat = GGL_PIXEL_FORMAT_RGBX_8888;
-        depthFormat = GGL_PIXEL_FORMAT_Z_16;
-        break;
-    case 4:
-        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
-        depthFormat = 0;
-        break;
-    case 5:
-        pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
-        depthFormat = GGL_PIXEL_FORMAT_Z_16;
-        break;
-    case 6:
-        pixelFormat = GGL_PIXEL_FORMAT_A_8;
-        depthFormat = 0;
-        break;
-    case 7:
-        pixelFormat = GGL_PIXEL_FORMAT_A_8;
-        depthFormat = GGL_PIXEL_FORMAT_Z_16;
-        break;
-    case 8:
-        pixelFormat = GGL_PIXEL_FORMAT_BGRA_8888;
-        depthFormat = 0;
-        break;
-    default:
-        return NAME_NOT_FOUND;
-    }
-    return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-template<typename T>
-static int binarySearch(T const sortedArray[], int first, int last, EGLint key)
-{
-   while (first <= last) {
-       int mid = (first + last) / 2;
-       if (key > sortedArray[mid].key) {
-           first = mid + 1;
-       } else if (key < sortedArray[mid].key) {
-           last = mid - 1;
-       } else {
-           return mid;
-       }
-   }
-   return -1;
-}
-
-static int isAttributeMatching(int i, EGLint attr, EGLint val)
-{
-    // look for the attribute in all of our configs
-    config_pair_t const* configFound = gConfigs[i].array;
-    int index = binarySearch<config_pair_t>(
-            gConfigs[i].array,
-            0, gConfigs[i].size-1,
-            attr);
-    if (index < 0) {
-        configFound = config_base_attribute_list;
-        index = binarySearch<config_pair_t>(
-                config_base_attribute_list,
-                0, NELEM(config_base_attribute_list)-1,
-                attr);
-    }
-    if (index >= 0) {
-        // attribute found, check if this config could match
-        int cfgMgtIndex = binarySearch<config_management_t>(
-                gConfigManagement,
-                0, NELEM(gConfigManagement)-1,
-                attr);
-        if (cfgMgtIndex >= 0) {
-            bool match = gConfigManagement[cfgMgtIndex].match(
-                    val, configFound[index].value);
-            if (match) {
-                // this config matches
-                return 1;
-            }
-        } else {
-            // attribute not found. this should NEVER happen.
-        }
-    } else {
-        // error, this attribute doesn't exist
-    }
-    return 0;
-}
-
-static int makeCurrent(ogles_context_t* gl)
-{
-    ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific();
-    if (gl) {
-        egl_context_t* c = egl_context_t::context(gl);
-        if (c->flags & egl_context_t::IS_CURRENT) {
-            if (current != gl) {
-                // it is an error to set a context current, if it's already
-                // current to another thread
-                return -1;
-            }
-        } else {
-            if (current) {
-                // mark the current context as not current, and flush
-                glFlush();
-                egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
-            }
-        }
-        if (!(c->flags & egl_context_t::IS_CURRENT)) {
-            // The context is not current, make it current!
-            setGlThreadSpecific(gl);
-            c->flags |= egl_context_t::IS_CURRENT;
-        }
-    } else {
-        if (current) {
-            // mark the current context as not current, and flush
-            glFlush();
-            egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;
-        }
-        // this thread has no context attached to it
-        setGlThreadSpecific(0);
-    }
-    return 0;
-}
-
-static EGLBoolean getConfigAttrib(EGLDisplay /*dpy*/, EGLConfig config,
-        EGLint attribute, EGLint *value)
-{
-    size_t numConfigs =  NELEM(gConfigs);
-    int index = (int)(uintptr_t)config;
-    if (uint32_t(index) >= numConfigs)
-        return setError(EGL_BAD_CONFIG, EGL_FALSE);
-
-    int attrIndex;
-    attrIndex = binarySearch<config_pair_t>(
-            gConfigs[index].array,
-            0, gConfigs[index].size-1,
-            attribute);
-    if (attrIndex>=0) {
-        *value = gConfigs[index].array[attrIndex].value;
-        return EGL_TRUE;
-    }
-
-    attrIndex = binarySearch<config_pair_t>(
-            config_base_attribute_list,
-            0, NELEM(config_base_attribute_list)-1,
-            attribute);
-    if (attrIndex>=0) {
-        *value = config_base_attribute_list[attrIndex].value;
-        return EGL_TRUE;
-    }
-    return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
-}
-
-static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
-        NativeWindowType window, const EGLint* /*attrib_list*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
-    if (window == 0)
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
-    EGLint surfaceType;
-    if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
-        return EGL_FALSE;
-
-    if (!(surfaceType & EGL_WINDOW_BIT))
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
-    if (static_cast<ANativeWindow*>(window)->common.magic !=
-            ANDROID_NATIVE_WINDOW_MAGIC) {
-        return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
-    }
-        
-    EGLint configID;
-    if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
-        return EGL_FALSE;
-
-    int32_t depthFormat;
-    int32_t pixelFormat;
-    if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) {
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-    }
-
-    // FIXME: we don't have access to the pixelFormat here just yet.
-    // (it's possible that the surface is not fully initialized)
-    // maybe this should be done after the page-flip
-    //if (EGLint(info.format) != pixelFormat)
-    //    return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
-    egl_surface_t* surface;
-    surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
-            static_cast<ANativeWindow*>(window));
-
-    if (!surface->initCheck()) {
-        // there was a problem in the ctor, the error
-        // flag has been set.
-        delete surface;
-        surface = 0;
-    }
-    return surface;
-}
-
-static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config,
-        NativePixmapType pixmap, const EGLint* /*attrib_list*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
-    if (pixmap == 0)
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
-    EGLint surfaceType;
-    if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
-        return EGL_FALSE;
-
-    if (!(surfaceType & EGL_PIXMAP_BIT))
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
-    if (static_cast<egl_native_pixmap_t*>(pixmap)->version != 
-            sizeof(egl_native_pixmap_t)) {
-        return setError(EGL_BAD_NATIVE_PIXMAP, EGL_NO_SURFACE);
-    }
-    
-    EGLint configID;
-    if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
-        return EGL_FALSE;
-
-    int32_t depthFormat;
-    int32_t pixelFormat;
-    if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) {
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-    }
-
-    if (pixmap->format != pixelFormat)
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
-    egl_surface_t* surface =
-        new egl_pixmap_surface_t(dpy, config, depthFormat,
-                static_cast<egl_native_pixmap_t*>(pixmap));
-
-    if (!surface->initCheck()) {
-        // there was a problem in the ctor, the error
-        // flag has been set.
-        delete surface;
-        surface = 0;
-    }
-    return surface;
-}
-
-static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config,
-        const EGLint *attrib_list)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
-
-    EGLint surfaceType;
-    if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
-        return EGL_FALSE;
-
-    if (!(surfaceType & EGL_PBUFFER_BIT))
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
-    EGLint configID;
-    if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
-        return EGL_FALSE;
-
-    int32_t depthFormat;
-    int32_t pixelFormat;
-    if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) {
-        return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-    }
-
-    int32_t w = 0;
-    int32_t h = 0;
-    while (attrib_list[0] != EGL_NONE) {
-        if (attrib_list[0] == EGL_WIDTH)  w = attrib_list[1];
-        if (attrib_list[0] == EGL_HEIGHT) h = attrib_list[1];
-        attrib_list+=2;
-    }
-
-    egl_surface_t* surface =
-        new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat);
-
-    if (!surface->initCheck()) {
-        // there was a problem in the ctor, the error
-        // flag has been set.
-        delete surface;
-        surface = 0;
-    }
-    return surface;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-// ----------------------------------------------------------------------------
-// Initialization
-// ----------------------------------------------------------------------------
-
-EGLDisplay eglGetDisplay(NativeDisplayType display)
-{
-#ifndef __ANDROID__
-    // this just needs to be done once
-    if (gGLKey == -1) {
-        pthread_mutex_lock(&gInitMutex);
-        if (gGLKey == -1)
-            pthread_key_create(&gGLKey, NULL);
-        pthread_mutex_unlock(&gInitMutex);
-    }
-#endif
-    if (display == EGL_DEFAULT_DISPLAY) {
-        EGLDisplay dpy = (EGLDisplay)1;
-        egl_display_t& d = egl_display_t::get_display(dpy);
-        d.type = display;
-        return dpy;
-    }
-    return EGL_NO_DISPLAY;
-}
-
-EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    EGLBoolean res = EGL_TRUE;
-    egl_display_t& d = egl_display_t::get_display(dpy);
-
-    if (d.initialized.fetch_add(1, std::memory_order_acquire) == 0) {
-        // initialize stuff here if needed
-        //pthread_mutex_lock(&gInitMutex);
-        //pthread_mutex_unlock(&gInitMutex);
-    }
-
-    if (res == EGL_TRUE) {
-        if (major != NULL) *major = VERSION_MAJOR;
-        if (minor != NULL) *minor = VERSION_MINOR;
-    }
-    return res;
-}
-
-EGLBoolean eglTerminate(EGLDisplay dpy)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    EGLBoolean res = EGL_TRUE;
-    egl_display_t& d = egl_display_t::get_display(dpy);
-    if (d.initialized.fetch_sub(1, std::memory_order_release) == 1) {
-        std::atomic_thread_fence(std::memory_order_acquire);
-        // TODO: destroy all resources (surfaces, contexts, etc...)
-        //pthread_mutex_lock(&gInitMutex);
-        //pthread_mutex_unlock(&gInitMutex);
-    }
-    return res;
-}
-
-// ----------------------------------------------------------------------------
-// configuration
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglGetConfigs(   EGLDisplay dpy,
-                            EGLConfig *configs,
-                            EGLint config_size, EGLint *num_config)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    if (ggl_unlikely(num_config==NULL))
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-
-    GLint numConfigs = NELEM(gConfigs);
-    if (!configs) {
-        *num_config = numConfigs;
-        return EGL_TRUE;
-    }
-    GLint i;
-    for (i=0 ; i<numConfigs && i<config_size ; i++) {
-        *configs++ = (EGLConfig)(uintptr_t)i;
-    }
-    *num_config = i;
-    return EGL_TRUE;
-}
-
-EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
-                            EGLConfig *configs, EGLint config_size,
-                            EGLint *num_config)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    if (ggl_unlikely(num_config==NULL)) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    }
-
-    if (ggl_unlikely(attrib_list==0)) {
-        /*
-         * A NULL attrib_list should be treated as though it was an empty
-         * one (terminated with EGL_NONE) as defined in
-         * section 3.4.1 "Querying Configurations" in the EGL specification.
-         */
-        static const EGLint dummy = EGL_NONE;
-        attrib_list = &dummy;
-    }
-
-    int numAttributes = 0;
-    int numConfigs =  NELEM(gConfigs);
-    uint32_t possibleMatch = (1<<numConfigs)-1;
-    while(possibleMatch && *attrib_list != EGL_NONE) {
-        numAttributes++;
-        EGLint attr = *attrib_list++;
-        EGLint val  = *attrib_list++;
-        for (int i=0 ; possibleMatch && i<numConfigs ; i++) {
-            if (!(possibleMatch & (1<<i)))
-                continue;
-            if (isAttributeMatching(i, attr, val) == 0) {
-                possibleMatch &= ~(1<<i);
-            }
-        }
-    }
-
-    // now, handle the attributes which have a useful default value
-    for (size_t j=0 ; possibleMatch && j<NELEM(config_defaults) ; j++) {
-        // see if this attribute was specified, if not, apply its
-        // default value
-        if (binarySearch<config_pair_t>(
-                (config_pair_t const*)attrib_list,
-                0, numAttributes-1,
-                config_defaults[j].key) < 0)
-        {
-            for (int i=0 ; possibleMatch && i<numConfigs ; i++) {
-                if (!(possibleMatch & (1<<i)))
-                    continue;
-                if (isAttributeMatching(i,
-                        config_defaults[j].key,
-                        config_defaults[j].value) == 0)
-                {
-                    possibleMatch &= ~(1<<i);
-                }
-            }
-        }
-    }
-
-    // return the configurations found
-    int n=0;
-    if (possibleMatch) {
-        if (configs) {
-            for (int i=0 ; config_size && i<numConfigs ; i++) {
-                if (possibleMatch & (1<<i)) {
-                    *configs++ = (EGLConfig)(uintptr_t)i;
-                    config_size--;
-                    n++;
-                }
-            }
-        } else {
-            for (int i=0 ; i<numConfigs ; i++) {
-                if (possibleMatch & (1<<i)) {
-                    n++;
-                }
-            }
-        }
-    }
-    *num_config = n;
-     return EGL_TRUE;
-}
-
-EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
-        EGLint attribute, EGLint *value)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    return getConfigAttrib(dpy, config, attribute, value);
-}
-
-// ----------------------------------------------------------------------------
-// surfaces
-// ----------------------------------------------------------------------------
-
-EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
-                                    NativeWindowType window,
-                                    const EGLint *attrib_list)
-{
-    return createWindowSurface(dpy, config, window, attrib_list);
-}
-
-EGLSurface eglCreatePixmapSurface(  EGLDisplay dpy, EGLConfig config,
-                                    NativePixmapType pixmap,
-                                    const EGLint *attrib_list)
-{
-    return createPixmapSurface(dpy, config, pixmap, attrib_list);
-}
-
-EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
-                                    const EGLint *attrib_list)
-{
-    return createPbufferSurface(dpy, config, attrib_list);
-}
-
-EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    if (eglSurface != EGL_NO_SURFACE) {
-        egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) );
-        if (!surface->isValid())
-            return setError(EGL_BAD_SURFACE, EGL_FALSE);
-        if (surface->dpy != dpy)
-            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        if (surface->ctx) {
-            // defer disconnect/delete until no longer current
-            surface->zombie = true;
-        } else {
-            surface->disconnect();
-            delete surface;
-        }
-    }
-    return EGL_TRUE;
-}
-
-EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface,
-                            EGLint attribute, EGLint *value)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    egl_surface_t* surface = static_cast<egl_surface_t*>(eglSurface);
-    if (!surface->isValid())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
-    if (surface->dpy != dpy)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    EGLBoolean ret = EGL_TRUE;
-    switch (attribute) {
-        case EGL_CONFIG_ID:
-            ret = getConfigAttrib(dpy, surface->config, EGL_CONFIG_ID, value);
-            break;
-        case EGL_WIDTH:
-            *value = surface->getWidth();
-            break;
-        case EGL_HEIGHT:
-            *value = surface->getHeight();
-            break;
-        case EGL_LARGEST_PBUFFER:
-            // not modified for a window or pixmap surface
-            break;
-        case EGL_TEXTURE_FORMAT:
-            *value = EGL_NO_TEXTURE;
-            break;
-        case EGL_TEXTURE_TARGET:
-            *value = EGL_NO_TEXTURE;
-            break;
-        case EGL_MIPMAP_TEXTURE:
-            *value = EGL_FALSE;
-            break;
-        case EGL_MIPMAP_LEVEL:
-            *value = 0;
-            break;
-        case EGL_RENDER_BUFFER:
-            // TODO: return the real RENDER_BUFFER here
-            *value = EGL_BACK_BUFFER;
-            break;
-        case EGL_HORIZONTAL_RESOLUTION:
-            // pixel/mm * EGL_DISPLAY_SCALING
-            *value = surface->getHorizontalResolution();
-            break;
-        case EGL_VERTICAL_RESOLUTION:
-            // pixel/mm * EGL_DISPLAY_SCALING
-            *value = surface->getVerticalResolution();
-            break;
-        case EGL_PIXEL_ASPECT_RATIO: {
-            // w/h * EGL_DISPLAY_SCALING
-            int wr = surface->getHorizontalResolution();
-            int hr = surface->getVerticalResolution();
-            *value = (wr * EGL_DISPLAY_SCALING) / hr;
-        } break;
-        case EGL_SWAP_BEHAVIOR:
-            *value = surface->getSwapBehavior();
-            break;
-        default:
-            ret = setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
-    }
-    return ret;
-}
-
-EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
-                            EGLContext /*share_list*/, const EGLint* /*attrib_list*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
-
-    ogles_context_t* gl = ogles_init(sizeof(egl_context_t));
-    if (!gl) return setError(EGL_BAD_ALLOC, EGL_NO_CONTEXT);
-
-    egl_context_t* c = static_cast<egl_context_t*>(gl->rasterizer.base);
-    c->flags = egl_context_t::NEVER_CURRENT;
-    c->dpy = dpy;
-    c->config = config;
-    c->read = 0;
-    c->draw = 0;
-    return (EGLContext)gl;
-}
-
-EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    egl_context_t* c = egl_context_t::context(ctx);
-    if (c->flags & egl_context_t::IS_CURRENT)
-        setGlThreadSpecific(0);
-    ogles_uninit((ogles_context_t*)ctx);
-    return EGL_TRUE;
-}
-
-EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
-                            EGLSurface read, EGLContext ctx)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    if (draw) {
-        egl_surface_t* s = (egl_surface_t*)draw;
-        if (!s->isValid())
-            return setError(EGL_BAD_SURFACE, EGL_FALSE);
-        if (s->dpy != dpy)
-            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        // TODO: check that draw is compatible with the context
-    }
-    if (read && read!=draw) {
-        egl_surface_t* s = (egl_surface_t*)read;
-        if (!s->isValid())
-            return setError(EGL_BAD_SURFACE, EGL_FALSE);
-        if (s->dpy != dpy)
-            return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-        // TODO: check that read is compatible with the context
-    }
-
-    EGLContext current_ctx = EGL_NO_CONTEXT;
-
-    if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT))
-        return setError(EGL_BAD_MATCH, EGL_FALSE);
-
-    if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT))
-        return setError(EGL_BAD_MATCH, EGL_FALSE);
-
-    if (ctx == EGL_NO_CONTEXT) {
-        // if we're detaching, we need the current context
-        current_ctx = (EGLContext)getGlThreadSpecific();
-    } else {
-        egl_surface_t* d = (egl_surface_t*)draw;
-        egl_surface_t* r = (egl_surface_t*)read;
-        if ((d && d->ctx && d->ctx != ctx) ||
-            (r && r->ctx && r->ctx != ctx)) {
-            // one of the surface is bound to a context in another thread
-            return setError(EGL_BAD_ACCESS, EGL_FALSE);
-        }
-    }
-
-    ogles_context_t* gl = (ogles_context_t*)ctx;
-    if (makeCurrent(gl) == 0) {
-        if (ctx) {
-            egl_context_t* c = egl_context_t::context(ctx);
-            egl_surface_t* d = (egl_surface_t*)draw;
-            egl_surface_t* r = (egl_surface_t*)read;
-            
-            if (c->draw) {
-                egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw);
-                s->disconnect();
-                s->ctx = EGL_NO_CONTEXT;
-                if (s->zombie)
-                    delete s;
-            }
-            if (c->read) {
-                // FIXME: unlock/disconnect the read surface too 
-            }
-            
-            c->draw = draw;
-            c->read = read;
-
-            if (c->flags & egl_context_t::NEVER_CURRENT) {
-                c->flags &= ~egl_context_t::NEVER_CURRENT;
-                GLint w = 0;
-                GLint h = 0;
-                if (draw) {
-                    w = d->getWidth();
-                    h = d->getHeight();
-                }
-                ogles_surfaceport(gl, 0, 0);
-                ogles_viewport(gl, 0, 0, w, h);
-                ogles_scissor(gl, 0, 0, w, h);
-            }
-            if (d) {
-                if (d->connect() == EGL_FALSE) {
-                    return EGL_FALSE;
-                }
-                d->ctx = ctx;
-                d->bindDrawSurface(gl);
-            }
-            if (r) {
-                // FIXME: lock/connect the read surface too 
-                r->ctx = ctx;
-                r->bindReadSurface(gl);
-            }
-        } else {
-            // if surfaces were bound to the context bound to this thread
-            // mark then as unbound.
-            if (current_ctx) {
-                egl_context_t* c = egl_context_t::context(current_ctx);
-                egl_surface_t* d = (egl_surface_t*)c->draw;
-                egl_surface_t* r = (egl_surface_t*)c->read;
-                if (d) {
-                    c->draw = 0;
-                    d->disconnect();
-                    d->ctx = EGL_NO_CONTEXT;
-                    if (d->zombie)
-                        delete d;
-                }
-                if (r) {
-                    c->read = 0;
-                    r->ctx = EGL_NO_CONTEXT;
-                    // FIXME: unlock/disconnect the read surface too 
-                }
-            }
-        }
-        return EGL_TRUE;
-    }
-    return setError(EGL_BAD_ACCESS, EGL_FALSE);
-}
-
-EGLContext eglGetCurrentContext(void)
-{
-    // eglGetCurrentContext returns the current EGL rendering context,
-    // as specified by eglMakeCurrent. If no context is current,
-    // EGL_NO_CONTEXT is returned.
-    return (EGLContext)getGlThreadSpecific();
-}
-
-EGLSurface eglGetCurrentSurface(EGLint readdraw)
-{
-    // eglGetCurrentSurface returns the read or draw surface attached
-    // to the current EGL rendering context, as specified by eglMakeCurrent.
-    // If no context is current, EGL_NO_SURFACE is returned.
-    EGLContext ctx = (EGLContext)getGlThreadSpecific();
-    if (ctx == EGL_NO_CONTEXT) return EGL_NO_SURFACE;
-    egl_context_t* c = egl_context_t::context(ctx);
-    if (readdraw == EGL_READ) {
-        return c->read;
-    } else if (readdraw == EGL_DRAW) {
-        return c->draw;
-    }
-    return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
-}
-
-EGLDisplay eglGetCurrentDisplay(void)
-{
-    // eglGetCurrentDisplay returns the current EGL display connection
-    // for the current EGL rendering context, as specified by eglMakeCurrent.
-    // If no context is current, EGL_NO_DISPLAY is returned.
-    EGLContext ctx = (EGLContext)getGlThreadSpecific();
-    if (ctx == EGL_NO_CONTEXT) return EGL_NO_DISPLAY;
-    egl_context_t* c = egl_context_t::context(ctx);
-    return c->dpy;
-}
-
-EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
-                            EGLint attribute, EGLint *value)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    egl_context_t* c = egl_context_t::context(ctx);
-    switch (attribute) {
-        case EGL_CONFIG_ID:
-            // Returns the ID of the EGL frame buffer configuration with
-            // respect to which the context was created
-            return getConfigAttrib(dpy, c->config, EGL_CONFIG_ID, value);
-    }
-    return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
-}
-
-EGLBoolean eglWaitGL(void)
-{
-    return EGL_TRUE;
-}
-
-EGLBoolean eglWaitNative(EGLint /*engine*/)
-{
-    return EGL_TRUE;
-}
-
-EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    egl_surface_t* d = static_cast<egl_surface_t*>(draw);
-    if (!d->isValid())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
-    if (d->dpy != dpy)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    // post the surface
-    d->swapBuffers();
-
-    // if it's bound to a context, update the buffer
-    if (d->ctx != EGL_NO_CONTEXT) {
-        d->bindDrawSurface((ogles_context_t*)d->ctx);
-        // if this surface is also the read surface of the context
-        // it is bound to, make sure to update the read buffer as well.
-        // The EGL spec is a little unclear about this.
-        egl_context_t* c = egl_context_t::context(d->ctx);
-        if (c->read == draw) {
-            d->bindReadSurface((ogles_context_t*)d->ctx);
-        }
-    }
-
-    return EGL_TRUE;
-}
-
-EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface /*surface*/,
-                            NativePixmapType /*target*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    // TODO: eglCopyBuffers()
-    return EGL_FALSE;
-}
-
-EGLint eglGetError(void)
-{
-    return getError();
-}
-
-const char* eglQueryString(EGLDisplay dpy, EGLint name)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, (const char*)0);
-
-    switch (name) {
-        case EGL_VENDOR:
-            return gVendorString;
-        case EGL_VERSION:
-            return gVersionString;
-        case EGL_EXTENSIONS:
-            return gExtensionsString;
-        case EGL_CLIENT_APIS:
-            return gClientApiString;
-    }
-    return setError(EGL_BAD_PARAMETER, (const char *)0);
-}
-
-// ----------------------------------------------------------------------------
-// EGL 1.1
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglSurfaceAttrib(
-        EGLDisplay dpy, EGLSurface /*surface*/, EGLint /*attribute*/, EGLint /*value*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    // TODO: eglSurfaceAttrib()
-    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-}
-
-EGLBoolean eglBindTexImage(
-        EGLDisplay dpy, EGLSurface /*surface*/, EGLint /*buffer*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    // TODO: eglBindTexImage()
-    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-}
-
-EGLBoolean eglReleaseTexImage(
-        EGLDisplay dpy, EGLSurface /*surface*/, EGLint /*buffer*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    // TODO: eglReleaseTexImage()
-    return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-}
-
-EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint /*interval*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    // TODO: eglSwapInterval()
-    return EGL_TRUE;
-}
-
-// ----------------------------------------------------------------------------
-// EGL 1.2
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglBindAPI(EGLenum api)
-{
-    if (api != EGL_OPENGL_ES_API)
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    return EGL_TRUE;
-}
-
-EGLenum eglQueryAPI(void)
-{
-    return EGL_OPENGL_ES_API;
-}
-
-EGLBoolean eglWaitClient(void)
-{
-    glFinish();
-    return EGL_TRUE;
-}
-
-EGLBoolean eglReleaseThread(void)
-{
-    // TODO: eglReleaseThread()
-    return EGL_TRUE;
-}
-
-EGLSurface eglCreatePbufferFromClientBuffer(
-          EGLDisplay dpy, EGLenum /*buftype*/, EGLClientBuffer /*buffer*/,
-          EGLConfig /*config*/, const EGLint* /*attrib_list*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE);
-    // TODO: eglCreatePbufferFromClientBuffer()
-    return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
-}
-
-// ----------------------------------------------------------------------------
-// EGL_EGLEXT_VERSION 3
-// ----------------------------------------------------------------------------
-
-void (*eglGetProcAddress (const char *procname))()
-{
-    extention_map_t const * const map = gExtentionMap;
-    for (uint32_t i=0 ; i<NELEM(gExtentionMap) ; i++) {
-        if (!strcmp(procname, map[i].name)) {
-            return map[i].address;
-        }
-    }
-    return NULL;
-}
-
-EGLBoolean eglLockSurfaceKHR(EGLDisplay /*dpy*/, EGLSurface /*surface*/,
-        const EGLint* /*attrib_list*/)
-{
-    EGLBoolean result = EGL_FALSE;
-    return result;
-}
-
-EGLBoolean eglUnlockSurfaceKHR(EGLDisplay /*dpy*/, EGLSurface /*surface*/)
-{
-    EGLBoolean result = EGL_FALSE;
-    return result;
-}
-
-EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
-        EGLClientBuffer buffer, const EGLint* /*attrib_list*/)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE) {
-        return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR);
-    }
-    if (ctx != EGL_NO_CONTEXT) {
-        return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
-    }
-    if (target != EGL_NATIVE_BUFFER_ANDROID) {
-        return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
-    }
-
-    ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)buffer;
-
-    if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
-        return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
-
-    if (native_buffer->common.version != sizeof(ANativeWindowBuffer))
-        return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
-
-    switch (native_buffer->format) {
-        case HAL_PIXEL_FORMAT_RGBA_8888:
-        case HAL_PIXEL_FORMAT_RGBX_8888:
-        case HAL_PIXEL_FORMAT_RGB_888:
-        case HAL_PIXEL_FORMAT_RGB_565:
-        case HAL_PIXEL_FORMAT_BGRA_8888:
-            break;
-        default:
-            return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
-    }
-
-    native_buffer->common.incRef(&native_buffer->common);
-    return (EGLImageKHR)native_buffer;
-}
-
-EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE) {
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-    }
-
-    ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)img;
-
-    if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-
-    if (native_buffer->common.version != sizeof(ANativeWindowBuffer))
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-
-    native_buffer->common.decRef(&native_buffer->common);
-
-    return EGL_TRUE;
-}
-
-// ----------------------------------------------------------------------------
-// EGL_KHR_fence_sync
-// ----------------------------------------------------------------------------
-
-#define FENCE_SYNC_HANDLE ((EGLSyncKHR)0xFE4CE)
-
-EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type,
-        const EGLint *attrib_list)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE) {
-        return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC_KHR);
-    }
-
-    if (type != EGL_SYNC_FENCE_KHR ||
-            (attrib_list != NULL && attrib_list[0] != EGL_NONE)) {
-        return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR);
-    }
-
-    if (eglGetCurrentContext() == EGL_NO_CONTEXT) {
-        return setError(EGL_BAD_MATCH, EGL_NO_SYNC_KHR);
-    }
-
-    // AGL is synchronous; nothing to do here.
-
-    return FENCE_SYNC_HANDLE;
-}
-
-EGLBoolean eglDestroySyncKHR(EGLDisplay /*dpy*/, EGLSyncKHR sync)
-{
-    if (sync != FENCE_SYNC_HANDLE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    }
-
-    return EGL_TRUE;
-}
-
-EGLint eglClientWaitSyncKHR(EGLDisplay /*dpy*/, EGLSyncKHR sync, EGLint /*flags*/,
-        EGLTimeKHR /*timeout*/)
-{
-    if (sync != FENCE_SYNC_HANDLE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    }
-
-    return EGL_CONDITION_SATISFIED_KHR;
-}
-
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay /*dpy*/, EGLSyncKHR sync,
-        EGLint attribute, EGLint *value)
-{
-    if (sync != FENCE_SYNC_HANDLE) {
-        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
-    }
-
-    switch (attribute) {
-    case EGL_SYNC_TYPE_KHR:
-        *value = EGL_SYNC_FENCE_KHR;
-        return EGL_TRUE;
-    case EGL_SYNC_STATUS_KHR:
-        *value = EGL_SIGNALED_KHR;
-        return EGL_TRUE;
-    case EGL_SYNC_CONDITION_KHR:
-        *value = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR;
-        return EGL_TRUE;
-    default:
-        return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
-    }
-}
-
-// ----------------------------------------------------------------------------
-// ANDROID extensions
-// ----------------------------------------------------------------------------
-
-EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
-        EGLint left, EGLint top, EGLint width, EGLint height)
-{
-    if (egl_display_t::is_valid(dpy) == EGL_FALSE)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    egl_surface_t* d = static_cast<egl_surface_t*>(draw);
-    if (!d->isValid())
-        return setError(EGL_BAD_SURFACE, EGL_FALSE);
-    if (d->dpy != dpy)
-        return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
-    // post the surface
-    d->setSwapRectangle(left, top, width, height);
-
-    return EGL_TRUE;
-}
diff --git a/opengl/libagl/fixed_asm.S b/opengl/libagl/fixed_asm.S
deleted file mode 100644
index 5e08856..0000000
--- a/opengl/libagl/fixed_asm.S
+++ /dev/null
@@ -1,67 +0,0 @@
-/* libs/opengles/fixed_asm.S
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-
-    .text
-    .align 2
-    
-    .global gglFloatToFixed
-    .type gglFloatToFixed, %function
-    .global gglFloatToFixedFast
-    .type gglFloatToFixedFast, %function
-
-
-/*
- * Converts a float to a s15.16 fixed-point number.
- * this doesn't handle floats out of the [-32768, +32768[ range
- * and doesn't performs round-to-nearest.
- * however, it's very fast :-)
- */
-
-gglFloatToFixedFast:
-        movs    r1, r0, lsl #1          /* remove bit sign */
-        mov     r2, #0x8E               /* 127 + 15 */
-        sub     r1, r2, r1, lsr #24     /* compute shift */
-        mov     r2, r0, lsl #8          /* mantissa<<8 */
-        orr     r2, r2, #0x80000000     /* add the missing 1 */
-        mov     r0, r2, lsr r1          /* scale to 16.16 */
-        rsbcs   r0, r0, #0              /* negate if needed */
-        bx      lr
-
-/*
- * this version rounds-to-nearest and saturates numbers
- * outside the range (but not NaNs).
- */
-
-gglFloatToFixed:
-        mov     r1, r0, lsl #1          /* remove bit sign */
-        mov     r2, #0x8E               /* 127 + 15 */
-        subs    r1, r2, r1, lsr #24     /* compute shift */
-        bls     0f                      /* too big */
-        mov     r2, r0, lsl #8          /* mantissa<<8 */
-        orr     r2, r2, #0x80000000     /* add the missing 1 */
-        mov     r3, r0
-        movs    r0, r2, lsr r1          /* scale to 16.16 */
-        addcs   r0, r0, #1              /* round-to-nearest */
-        tst     r3, #0x80000000         /* negative? */
-        rsbne   r0, r0, #0              /* negate if needed */
-        bx      lr 
- 
-0:      ands    r0, r0, #0x80000000     /* keep only the sign bit */
-        moveq   r0, #0x7fffffff         /* positive, maximum value */
-        bx      lr
-
diff --git a/opengl/libagl/fp.cpp b/opengl/libagl/fp.cpp
deleted file mode 100644
index a7a4f7b..0000000
--- a/opengl/libagl/fp.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/* libs/opengles/fp.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 "fp.h"
-
-// ----------------------------------------------------------------------------
-
-#if !(defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6))
-GGLfixed gglFloatToFixed(float v) {   
-    return GGLfixed(floorf(v * 65536.0f + 0.5f));
-}
-#endif
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-namespace gl {
-
-GLfloat fixedToFloat(GLfixed x)
-{
-#if DEBUG_USE_FLOATS
-    return x / 65536.0f;
-#else
-    if (!x) return 0;
-    const uint32_t s = x & 0x80000000;
-    union {
-        uint32_t i;
-        float f;
-    };
-    i = s ? -x : x;
-    const int c = gglClz(i) - 8;
-    i = (c>=0) ? (i<<c) : (i>>-c);
-    const uint32_t e = 134 - c;
-    i &= ~0x800000;
-    i |= e<<23;
-    i |= s;
-    return f;
-#endif
-}
-
-float sinef(float x)
-{
-    const float A =   1.0f / (2.0f*M_PI);
-    const float B = -16.0f;
-    const float C =   8.0f;
-
-    // scale angle for easy argument reduction
-    x *= A;
-    
-    if (fabsf(x) >= 0.5f) {
-        // Argument reduction
-        x = x - ceilf(x + 0.5f) + 1.0f; 
-    }
-
-    const float y = B*x*fabsf(x) + C*x;
-    return 0.2215f * (y*fabsf(y) - y) + y;
-}
-
-float cosinef(float x)
-{
-    return sinef(x + float(M_PI/2));
-}
-
-void sincosf(GLfloat angle, GLfloat* s, GLfloat* c) {
-    *s = sinef(angle);
-    *c = cosinef(angle);
-}
-
-}; // namespace fp_utils
-
-// ----------------------------------------------------------------------------
-}; // namespace android
diff --git a/opengl/libagl/fp.h b/opengl/libagl/fp.h
deleted file mode 100644
index 6d0c183..0000000
--- a/opengl/libagl/fp.h
+++ /dev/null
@@ -1,243 +0,0 @@
-/* libs/opengles/fp.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_FP_H
-#define ANDROID_OPENGLES_FP_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-#include <math.h>
-
-#include <private/pixelflinger/ggl_context.h>
-
-#include <GLES/gl.h>
-
-#define DEBUG_USE_FLOATS      0
-
-// ----------------------------------------------------------------------------
-
-extern "C" GLfixed gglFloatToFixed(float f) __attribute__((const));
-
-// ----------------------------------------------------------------------------
-namespace android {
-
-namespace gl {
-
-        GLfloat fixedToFloat(GLfixed) CONST;
-
-        void    sincosf(GLfloat angle, GLfloat* s, GLfloat* c);
-        float   sinef(GLfloat x) CONST;
-        float   cosinef(GLfloat x) CONST;
-
-inline bool     cmpf(GLfloat a, GLfloat b) CONST;
-inline bool     isZerof(GLfloat) CONST;
-inline bool     isOnef(GLfloat) CONST;
-
-inline int      isZeroOrNegativef(GLfloat) CONST;
-
-inline int      exponent(GLfloat) CONST;
-inline int32_t  mantissa(GLfloat) CONST;
-inline GLfloat  clampToZerof(GLfloat) CONST;
-inline GLfloat  reciprocalf(GLfloat) CONST;
-inline GLfloat  rsqrtf(GLfloat) CONST;
-inline GLfloat  sqrf(GLfloat) CONST;
-inline GLfloat  addExpf(GLfloat v, int e) CONST;
-inline GLfloat  mul2f(GLfloat v) CONST;
-inline GLfloat  div2f(GLfloat v) CONST;
-inline GLfloat  absf(GLfloat v) CONST;
-
-
-/* 
- * float fastexpf(float) : a fast approximation of expf(x)
- *		give somewhat accurate results for -88 <= x <= 88
- *
- * exp(x) = 2^(x/ln(2))
- * we use the properties of float encoding
- * to get a fast 2^ and linear interpolation
- *
- */
-
-inline float fastexpf(float y) __attribute__((const));
-
-inline float fastexpf(float y)
-{
-	union {
-		float	r;
-		int32_t	i;
-	} u;	
-
-	// 127*ln(2) = 88
-	if (y < -88.0f) {
-		u.r = 0.0f;
-	} else if (y > 88.0f) {
-		u.r = INFINITY;
-	} else {
-		const float kOneOverLogTwo = (1L<<23) / M_LN2;
-		const int32_t kExponentBias = 127L<<23;
-		const int32_t e = int32_t(y*kOneOverLogTwo);
-		u.i = e + kExponentBias;
-	}
-	
-	return u.r;
-}
-
-
-bool cmpf(GLfloat a, GLfloat b) {
-#if DEBUG_USE_FLOATS
-    return a == b;
-#else
-    union {
-        float       f;
-        uint32_t    i;
-    } ua, ub;
-    ua.f = a;
-    ub.f = b;
-    return ua.i == ub.i;
-#endif
-} 
-
-bool isZerof(GLfloat v) {
-#if DEBUG_USE_FLOATS
-    return v == 0;
-#else
-    union {
-        float       f;
-        int32_t     i;
-    };
-    f = v;
-    return (i<<1) == 0;
-#endif
-}
-
-bool isOnef(GLfloat v) {
-    return cmpf(v, 1.0f);
-}
-
-int isZeroOrNegativef(GLfloat v) {
-#if DEBUG_USE_FLOATS
-    return v <= 0;
-#else
-    union {
-        float       f;
-        int32_t     i;
-    };
-    f = v;
-    return isZerof(v) | (i>>31);
-#endif
-}
-
-int exponent(GLfloat v) {
-    union {
-        float    f;
-        uint32_t i;
-    };
-    f = v;
-    return ((i << 1) >> 24) - 127;
-}
-
-int32_t mantissa(GLfloat v) {
-    union {
-        float    f;
-        uint32_t i;
-    };
-    f = v;
-    if (!(i&0x7F800000)) return 0;
-    const int s = i >> 31;
-    i |= (1L<<23);
-    i &= ~0xFF000000;
-    return s ? -i : i;
-}
-
-GLfloat clampToZerof(GLfloat v) {
-#if DEBUG_USE_FLOATS
-    return v<0 ? 0 : (v>1 ? 1 : v);
-#else
-    union {
-        float       f;
-        int32_t     i;
-    };
-    f = v;
-    i &= ~(i>>31);
-    return f;
-#endif
-}
-
-GLfloat reciprocalf(GLfloat v) {
-    // XXX: do better
-    return 1.0f / v;
-}
-
-GLfloat rsqrtf(GLfloat v) {
-    // XXX: do better
-    return 1.0f / sqrtf(v);
-}
-
-GLfloat sqrf(GLfloat v) {
-    // XXX: do better
-    return v*v;
-}
-
-GLfloat addExpf(GLfloat v, int e) {
-    union {
-        float       f;
-        int32_t     i;
-    };
-    f = v;
-    if (i<<1) { // XXX: deal with over/underflow	
-        i += int32_t(e)<<23;
-    }
-    return f;
-}
-
-GLfloat mul2f(GLfloat v) {
-#if DEBUG_USE_FLOATS
-    return v*2;
-#else
-    return addExpf(v, 1);
-#endif
-}
-
-GLfloat div2f(GLfloat v) {
-#if DEBUG_USE_FLOATS
-    return v*0.5f;
-#else
-    return addExpf(v, -1);
-#endif
-}
-
-GLfloat  absf(GLfloat v) {
-#if DEBUG_USE_FLOATS
-    return v<0 ? -v : v;
-#else
-    union {
-        float       f;
-        int32_t     i;
-    };
-    f = v;
-    i &= ~0x80000000;
-    return f;
-#endif
-}
-
-};  // namespace gl
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_FP_H
-
diff --git a/opengl/libagl/iterators.S b/opengl/libagl/iterators.S
deleted file mode 100644
index 8fe9039..0000000
--- a/opengl/libagl/iterators.S
+++ /dev/null
@@ -1,89 +0,0 @@
-/* libs/opengles/iterators.S
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
-
-
-    .text
-    .align 2
-    .arm
-    
-    .global iterators0032
-    .type iterators0032, %function
-
-/*
- * iterators0032
- *
- * MUST BE CALLED FROM ARM CODE
- *
- * r0: const compute_iterators_t* (this)
- *      r0 + 0: m_dx01 
- *      r0 + 4: m_dy10
- *      r0 + 8: m_dx20
- *      r0 +12: m_dy02
- *      r0 +16: m_x0
- *      r0 +20: m_y0
- *      r0 +24: m_area
- *		r0 +28: m_scale
- *		r0 +29: m_area_scale;
- * r1: int32_t* (out)
- *      r1 + 0: c
- *      r1 + 4: dcdx
- *      r1 + 8: dcdy
- *   r2: c0
- *   r3: c1
- * [sp]: c2
- */
- 
-iterators0032:
-        stmfd	sp!, {r4, r5, r6, r7, r8, lr}
-        ldr     r4, [sp, #4*6]
-
-        ldrb    r12, [r0, #29]
-        sub     r3, r3, r2
-        sub     r4, r4, r2
-        sub     r12, r12, #16
-        mov     r3, r3, asr r12
-        mov     r4, r4, asr r12
-        
-        ldr     r5, [r0, #0]
-        ldr     r12, [r0, #4]
-        smull   r8, lr, r4, r5
-        ldr     r5, [r0, #8]
-        smull   r6, r7, r4, r12
-        ldr     r12, [r0, #12]
-        smlal   r8, lr, r3, r5
-        smlal   r6, r7, r3, r12
-
-        ldr     r3, [r0, #16]        // m_x0
-        ldr     r4, [r0, #20]        // m_x1
-        
-        str     r6, [r1, #4]
-        str     r8, [r1, #8]
-
-        umull   r6, r5, r3, r6
-        umull   r8, r0, r4, r8
-        mla     r7, r3, r7, r5
-        mla     lr, r4, lr, r0
-        adds    r6, r6, r8
-        adc     r7, r7, lr
-
-        movs    r6, r6, lsr #4
-        adc     r6, r6, r7, lsl #28
-        rsb     r6, r6, r2, lsl #16
-        str     r6, [r1, #0]
-
-        ldmfd	sp!, {r4, r5, r6, r7, r8, pc}
-
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
deleted file mode 100644
index 216c725..0000000
--- a/opengl/libagl/light.cpp
+++ /dev/null
@@ -1,882 +0,0 @@
-/* libs/opengles/light.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 <stdio.h>
-#include "context.h"
-#include "fp.h"
-#include "light.h"
-#include "state.h"
-#include "matrix.h"
-
-
-#if defined(__arm__) && defined(__thumb__)
-#warning "light.cpp should not be compiled in thumb on ARM."
-#endif
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static void invalidate_lighting(ogles_context_t* c);
-static void lightVertexValidate(ogles_context_t* c, vertex_t* v);
-static void lightVertexNop(ogles_context_t* c, vertex_t* v);
-static void lightVertex(ogles_context_t* c, vertex_t* v);
-static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);
-
-static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);
-
-static __attribute__((noinline))
-void vnorm3(GLfixed* d, const GLfixed* a);
-
-static inline void vsa3(GLfixed* d,
-    const GLfixed* m, GLfixed s, const GLfixed* a);
-static inline void vss3(GLfixed* d,
-    const GLfixed* m, GLfixed s, const GLfixed* a);
-static inline void vmla3(GLfixed* d,
-    const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
-static inline void vmul3(GLfixed* d,
-    const GLfixed* m0, const GLfixed* m1);
-
-static GLfixed fog_linear(ogles_context_t* c, GLfixed z);
-static GLfixed fog_exp(ogles_context_t* c, GLfixed z);
-static GLfixed fog_exp2(ogles_context_t* c, GLfixed z);
-
-
-// ----------------------------------------------------------------------------
-
-static void init_white(vec4_t& c) {
-    c.r = c.g = c.b = c.a = 0x10000;
-}
-
-void ogles_init_light(ogles_context_t* c)
-{
-    for (unsigned int i=0 ; i<OGLES_MAX_LIGHTS ; i++) {
-        c->lighting.lights[i].ambient.a = 0x10000;
-        c->lighting.lights[i].position.z = 0x10000;
-        c->lighting.lights[i].spotDir.z = -0x10000;
-        c->lighting.lights[i].spotCutoff = gglIntToFixed(180);
-        c->lighting.lights[i].attenuation[0] = 0x10000;
-    }
-    init_white(c->lighting.lights[0].diffuse);
-    init_white(c->lighting.lights[0].specular);
-
-    c->lighting.front.ambient.r =
-    c->lighting.front.ambient.g =
-    c->lighting.front.ambient.b = gglFloatToFixed(0.2f);
-    c->lighting.front.ambient.a = 0x10000;
-    c->lighting.front.diffuse.r =
-    c->lighting.front.diffuse.g =
-    c->lighting.front.diffuse.b = gglFloatToFixed(0.8f);
-    c->lighting.front.diffuse.a = 0x10000;
-    c->lighting.front.specular.a = 0x10000;
-    c->lighting.front.emission.a = 0x10000;
-
-    c->lighting.lightModel.ambient.r =
-    c->lighting.lightModel.ambient.g =
-    c->lighting.lightModel.ambient.b = gglFloatToFixed(0.2f);
-    c->lighting.lightModel.ambient.a = 0x10000;
-
-    c->lighting.colorMaterial.face = GL_FRONT_AND_BACK;
-    c->lighting.colorMaterial.mode = GL_AMBIENT_AND_DIFFUSE;
-
-    c->fog.mode = GL_EXP;
-    c->fog.fog = fog_exp;
-    c->fog.density = 0x10000;
-    c->fog.end = 0x10000;
-    c->fog.invEndMinusStart = 0x10000;
-
-    invalidate_lighting(c);
-       
-    c->rasterizer.procs.shadeModel(c, GL_SMOOTH);
-    c->lighting.shadeModel = GL_SMOOTH;
-}
-
-void ogles_uninit_light(ogles_context_t* /*c*/)
-{
-}
-
-static inline int32_t clampF(GLfixed f) CONST;
-int32_t clampF(GLfixed f) {
-    f = (f & ~(f>>31));
-    if (f >= 0x10000)
-        f = 0x10000;
-    return f;
-}
-
-static GLfixed fog_linear(ogles_context_t* c, GLfixed z) {
-    return clampF(gglMulx((c->fog.end - ((z<0)?-z:z)), c->fog.invEndMinusStart));
-}
-
-static GLfixed fog_exp(ogles_context_t* c, GLfixed z) {
-    const float e = fixedToFloat(gglMulx(c->fog.density, ((z<0)?-z:z)));
-    return clampF(gglFloatToFixed(fastexpf(-e)));
-}
-
-static GLfixed fog_exp2(ogles_context_t* c, GLfixed z) {
-    const float e = fixedToFloat(gglMulx(c->fog.density, z));
-    return clampF(gglFloatToFixed(fastexpf(-e*e)));
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark math helpers
-#endif
-
-static inline
-void vscale3(GLfixed* d, const GLfixed* m, GLfixed s) {
-    d[0] = gglMulx(m[0], s);
-    d[1] = gglMulx(m[1], s);
-    d[2] = gglMulx(m[2], s);
-}
-
-static inline
-void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
-    d[0] = gglMulAddx(m[0], s, a[0]);
-    d[1] = gglMulAddx(m[1], s, a[1]);
-    d[2] = gglMulAddx(m[2], s, a[2]);
-}
-
-static inline
-void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
-    d[0] = gglMulSubx(m[0], s, a[0]);
-    d[1] = gglMulSubx(m[1], s, a[1]);
-    d[2] = gglMulSubx(m[2], s, a[2]);
-}
-
-static inline
-void vmla3(GLfixed* d,
-        const GLfixed* m0, const GLfixed* m1, const GLfixed* a)
-{
-    d[0] = gglMulAddx(m0[0], m1[0], a[0]);
-    d[1] = gglMulAddx(m0[1], m1[1], a[1]);
-    d[2] = gglMulAddx(m0[2], m1[2], a[2]);
-}
-
-static inline
-void vmul3(GLfixed* d, const GLfixed* m0, const GLfixed* m1) {
-    d[0] = gglMulx(m0[0], m1[0]);
-    d[1] = gglMulx(m0[1], m1[1]);
-    d[2] = gglMulx(m0[2], m1[2]);
-}
-
-void vnorm3(GLfixed* d, const GLfixed* a)
-{
-    // we must take care of overflows when normalizing a vector
-    GLfixed n;
-    int32_t x = a[0];   x = x>=0 ? x : -x;
-    int32_t y = a[1];   y = y>=0 ? y : -y;
-    int32_t z = a[2];   z = z>=0 ? z : -z;
-    if (ggl_likely(x<=0x6800 && y<=0x6800 && z<= 0x6800)) {
-        // in this case this will all fit on 32 bits
-        n = x*x + y*y + z*z;
-        n = gglSqrtRecipx(n);
-        n <<= 8;
-    } else {
-        // here norm^2 is at least 0x7EC00000 (>>32 == 0.495117)
-        n = vsquare3(x, y, z);
-        n = gglSqrtRecipx(n);
-    }
-    vscale3(d, a, n);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark lighting equations
-#endif
-
-static inline void light_picker(ogles_context_t* c)
-{
-    if (ggl_likely(!c->lighting.enable)) {
-        c->lighting.lightVertex = lightVertexNop;
-        return;
-    }
-    if (c->lighting.colorMaterial.enable) {
-        c->lighting.lightVertex = lightVertexMaterial;
-    } else {
-        c->lighting.lightVertex = lightVertex;
-    }
-}
-
-static inline void validate_light_mvi(ogles_context_t* c)
-{
-    uint32_t en = c->lighting.enabledLights;
-    // Vector from object to viewer, in eye coordinates
-    while (en) {
-        const int i = 31 - gglClz(en);
-        en &= ~(1<<i);
-        light_t& l = c->lighting.lights[i];
-#if OBJECT_SPACE_LIGHTING
-        c->transforms.mvui.point4(&c->transforms.mvui,
-                &l.objPosition, &l.position);
-#else
-        l.objPosition = l.position;
-#endif
-        vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
-    }
-    const vec4_t eyeViewer = {{{ 0, 0, 0x10000, 0 }}};
-#if OBJECT_SPACE_LIGHTING
-    c->transforms.mvui.point3(&c->transforms.mvui,
-            &c->lighting.objViewer, &eyeViewer);
-    vnorm3(c->lighting.objViewer.v, c->lighting.objViewer.v);
-#else
-    c->lighting.objViewer = eyeViewer;
-#endif
-}
-
-static inline void validate_light(ogles_context_t* c)
-{
-    // if colorMaterial is enabled, we get the color from the vertex
-    if (!c->lighting.colorMaterial.enable) {
-        material_t& material = c->lighting.front;
-        uint32_t en = c->lighting.enabledLights;
-        while (en) {
-            const int i = 31 - gglClz(en);
-            en &= ~(1<<i);
-            light_t& l = c->lighting.lights[i];
-            vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
-            vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
-            vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
-            
-            // this is just a flag to tell if we have a specular component
-            l.implicitSpecular.v[3] =
-                    l.implicitSpecular.r |
-                    l.implicitSpecular.g |
-                    l.implicitSpecular.b;
-            
-            l.rConstAttenuation = (l.attenuation[1] | l.attenuation[2])==0;
-            if (l.rConstAttenuation)
-                l.rConstAttenuation = gglRecipFast(l.attenuation[0]);
-        }
-        // emission and ambient for the whole scene
-        vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
-                c->lighting.lightModel.ambient.v,
-                material.ambient.v, 
-                material.emission.v);
-        c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a;
-    }
-    validate_light_mvi(c);
-}
-
-void invalidate_lighting(ogles_context_t* c)
-{
-    // TODO: pick lightVertexValidate or lightVertexValidateMVI
-    // instead of systematically the heavier lightVertexValidate()
-    c->lighting.lightVertex = lightVertexValidate;
-}
-
-void ogles_invalidate_lighting_mvui(ogles_context_t* c)
-{
-    invalidate_lighting(c);
-}
-
-void lightVertexNop(ogles_context_t*, vertex_t* /*v*/)
-{
-    // we should never end-up here
-}
-
-void lightVertexValidateMVI(ogles_context_t* c, vertex_t* v)
-{
-    validate_light_mvi(c);
-    light_picker(c);
-    c->lighting.lightVertex(c, v);
-}
-
-void lightVertexValidate(ogles_context_t* c, vertex_t* v)
-{
-    validate_light(c);
-    light_picker(c);
-    c->lighting.lightVertex(c, v);
-}
-
-void lightVertexMaterial(ogles_context_t* c, vertex_t* v)
-{
-    // fetch the material color
-    const GLvoid* cp = c->arrays.color.element(
-            v->index & vertex_cache_t::INDEX_MASK);
-    c->arrays.color.fetch(c, v->color.v, cp);
-
-    // acquire the color-material from the vertex
-    material_t& material = c->lighting.front;
-    material.ambient =
-    material.diffuse = v->color;
-    // implicit arguments need to be computed per/vertex
-    uint32_t en = c->lighting.enabledLights;
-    while (en) {
-        const int i = 31 - gglClz(en);
-        en &= ~(1<<i);
-        light_t& l = c->lighting.lights[i];
-        vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
-        vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
-        vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
-        // this is just a flag to tell if we have a specular component
-        l.implicitSpecular.v[3] =
-                l.implicitSpecular.r |
-                l.implicitSpecular.g |
-                l.implicitSpecular.b;
-    }
-    // emission and ambient for the whole scene
-    vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
-            c->lighting.lightModel.ambient.v,
-            material.ambient.v, 
-            material.emission.v);
-    c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a;
-
-    // now we can light our vertex as usual
-    lightVertex(c, v);
-}
-
-void lightVertex(ogles_context_t* c, vertex_t* v)
-{
-    // emission and ambient for the whole scene
-    vec4_t r = c->lighting.implicitSceneEmissionAndAmbient;
-    const vec4_t objViewer = c->lighting.objViewer;
-
-    uint32_t en = c->lighting.enabledLights;
-    if (ggl_likely(en)) {
-        // since we do the lighting in object-space, we don't need to
-        // transform each normal. However, we might still have to normalize
-        // it if GL_NORMALIZE is enabled.
-        vec4_t n;
-        c->arrays.normal.fetch(c, n.v,
-            c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
-
-#if !OBJECT_SPACE_LIGHTING
-        c->transforms.mvui.point3(&c->transforms.mvui, &n, &n);
-#endif
-
-        // TODO: right now we handle GL_RESCALE_NORMALS as if it were
-        // GL_NORMALIZE. We could optimize this by  scaling mvui 
-        // appropriately instead.
-        if (c->transforms.rescaleNormals)
-            vnorm3(n.v, n.v);
-
-        const material_t& material = c->lighting.front;
-        const int twoSide = c->lighting.lightModel.twoSide;
-
-        while (en) {
-            const int i = 31 - gglClz(en);
-            en &= ~(1<<i);
-            const light_t& l = c->lighting.lights[i];
-            
-            vec4_t d, t;
-            GLfixed s;
-            GLfixed sqDist = 0x10000;
-
-            // compute vertex-to-light vector
-            if (ggl_unlikely(l.position.w)) {
-                // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
-#if !OBJECT_SPACE_LIGHTING
-                vec4_t o;
-                const transform_t& mv = c->transforms.modelview.transform;
-                mv.point4(&mv, &o, &v->obj);
-                vss3(d.v, l.objPosition.v, o.w, o.v);
-#else
-                vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
-#endif
-                sqDist = dot3(d.v, d.v);
-                vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
-            } else {
-                // TODO: avoid copy here
-                d = l.normalizedObjPosition;
-            }
-
-            // ambient & diffuse
-            s = dot3(n.v, d.v);
-            s = (s<0) ? (twoSide?(-s):0) : s;
-            vsa3(t.v, l.implicitDiffuse.v, s, l.implicitAmbient.v);
-
-            // specular
-            if (ggl_unlikely(s && l.implicitSpecular.v[3])) {
-                vec4_t h;
-                h.x = d.x + objViewer.x;
-                h.y = d.y + objViewer.y;
-                h.z = d.z + objViewer.z;
-                vnorm3(h.v, h.v);
-                s = dot3(n.v, h.v);
-                s = (s<0) ? (twoSide?(-s):0) : s;
-                if (s > 0) {
-                    s = gglPowx(s, material.shininess);
-                    vsa3(t.v, l.implicitSpecular.v, s, t.v);
-                }
-            }
-
-            // spot
-            if (ggl_unlikely(l.spotCutoff != gglIntToFixed(180))) {
-                GLfixed spotAtt = -dot3(l.normalizedSpotDir.v, d.v);
-                if (spotAtt >= l.spotCutoffCosine) {
-                    vscale3(t.v, t.v, gglPowx(spotAtt, l.spotExp));
-                }
-            }
-
-            // attenuation
-            if (ggl_unlikely(l.position.w)) {
-                if (l.rConstAttenuation) {
-                    s = l.rConstAttenuation;
-                } else {
-                    s = gglMulAddx(sqDist, l.attenuation[2], l.attenuation[0]);
-                    if (l.attenuation[1])
-                        s = gglMulAddx(gglSqrtx(sqDist), l.attenuation[1], s);
-                    s = gglRecipFast(s);
-                }
-                vscale3(t.v, t.v, s);
-            }
-
-            r.r += t.r;
-            r.g += t.g;
-            r.b += t.b;
-        }
-    }
-    v->color.r = gglClampx(r.r);
-    v->color.g = gglClampx(r.g);
-    v->color.b = gglClampx(r.b);
-    v->color.a = gglClampx(r.a);
-    v->flags |= vertex_t::LIT;
-}
-
-static void lightModelx(GLenum pname, GLfixed param, ogles_context_t* c)
-{
-    if (ggl_unlikely(pname != GL_LIGHT_MODEL_TWO_SIDE)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->lighting.lightModel.twoSide = param ? GL_TRUE : GL_FALSE;
-    invalidate_lighting(c);
-}
-
-static void lightx(GLenum i, GLenum pname, GLfixed param, ogles_context_t* c)
-{
-    if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    light_t& light = c->lighting.lights[i-GL_LIGHT0];
-    switch (pname) {
-    case GL_SPOT_EXPONENT:
-        if (GGLfixed(param) >= gglIntToFixed(128)) {
-            ogles_error(c, GL_INVALID_VALUE);
-            return;
-        }
-        light.spotExp = param;
-        break;
-    case GL_SPOT_CUTOFF:
-        if (param!=gglIntToFixed(180) && GGLfixed(param)>=gglIntToFixed(90)) {
-            ogles_error(c, GL_INVALID_VALUE);
-            return;
-        }
-        light.spotCutoff = param;
-        light.spotCutoffCosine = 
-                gglFloatToFixed(cosinef((M_PI/(180.0f*65536.0f))*param));
-        break;
-    case GL_CONSTANT_ATTENUATION:
-        if (param < 0) {
-            ogles_error(c, GL_INVALID_VALUE);
-            return;
-        }
-        light.attenuation[0] = param;
-        break;
-    case GL_LINEAR_ATTENUATION:
-        if (param < 0) {
-            ogles_error(c, GL_INVALID_VALUE);
-            return;
-        }
-        light.attenuation[1] = param;
-        break;
-    case GL_QUADRATIC_ATTENUATION:
-        if (param < 0) {
-            ogles_error(c, GL_INVALID_VALUE);
-            return;
-        }
-        light.attenuation[2] = param;
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    invalidate_lighting(c);
-}
-
-static void lightxv(GLenum i, GLenum pname, const GLfixed *params, ogles_context_t* c)
-{
-    if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    GLfixed* what;
-    light_t& light = c->lighting.lights[i-GL_LIGHT0];
-    switch (pname) {
-    case GL_AMBIENT:
-        what = light.ambient.v;
-        break;
-    case GL_DIFFUSE:
-        what = light.diffuse.v;
-        break;
-    case GL_SPECULAR:
-        what = light.specular.v;
-        break;
-    case GL_POSITION: {
-        ogles_validate_transform(c, transform_state_t::MODELVIEW);
-        transform_t& mv = c->transforms.modelview.transform;
-        mv.point4(&mv, &light.position, reinterpret_cast<vec4_t const*>(params));
-        invalidate_lighting(c);
-        return;
-    }
-    case GL_SPOT_DIRECTION: {
-#if OBJECT_SPACE_LIGHTING
-        ogles_validate_transform(c, transform_state_t::MVUI);
-        transform_t& mvui = c->transforms.mvui;
-        mvui.point3(&mvui, &light.spotDir, reinterpret_cast<vec4_t const*>(params));
-#else
-        light.spotDir = *reinterpret_cast<vec4_t const*>(params);
-#endif
-        vnorm3(light.normalizedSpotDir.v, light.spotDir.v);
-        invalidate_lighting(c);
-        return;
-    }
-    default:
-        lightx(i, pname, params[0], c);
-        return;
-    }
-    what[0] = params[0];
-    what[1] = params[1];
-    what[2] = params[2];
-    what[3] = params[3];
-    invalidate_lighting(c);
-}
-
-static void materialx(GLenum face, GLenum pname, GLfixed param, ogles_context_t* c)
-{
-    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (ggl_unlikely(pname != GL_SHININESS)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->lighting.front.shininess = param;
-    invalidate_lighting(c);
-}
-
-static void fogx(GLenum pname, GLfixed param, ogles_context_t* c)
-{
-    switch (pname) {
-    case GL_FOG_DENSITY:
-        if (param >= 0) {
-            c->fog.density = param;
-            break;
-        }
-        ogles_error(c, GL_INVALID_VALUE);
-        break;
-    case GL_FOG_START:
-        c->fog.start = param;
-        c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start);
-        break;
-    case GL_FOG_END:
-        c->fog.end = param;
-        c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start);
-        break;
-    case GL_FOG_MODE:
-        switch (param) {
-        case GL_LINEAR:
-            c->fog.mode = param;
-            c->fog.fog = fog_linear;
-            break;
-        case GL_EXP:
-            c->fog.mode = param;
-            c->fog.fog = fog_exp;
-            break;
-        case GL_EXP2:
-            c->fog.mode = param;
-            c->fog.fog = fog_exp2;
-            break;
-        default:
-            ogles_error(c, GL_INVALID_ENUM);
-            break;
-        }
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        break;
-    }
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-#if 0
-#pragma mark -
-#pragma mark lighting APIs
-#endif
-
-void glShadeModel(GLenum mode)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (ggl_unlikely(mode != GL_SMOOTH && mode != GL_FLAT)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->lighting.shadeModel = mode;
-}
-
-void glLightModelf(GLenum pname, GLfloat param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    lightModelx(pname, gglFloatToFixed(param), c);
-}
-
-void glLightModelx(GLenum pname, GLfixed param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    lightModelx(pname, param, c);
-}
-
-void glLightModelfv(GLenum pname, const GLfloat *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (pname == GL_LIGHT_MODEL_TWO_SIDE) {
-        lightModelx(pname, gglFloatToFixed(params[0]), c);
-        return;
-    }
-
-    if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    c->lighting.lightModel.ambient.r = gglFloatToFixed(params[0]);
-    c->lighting.lightModel.ambient.g = gglFloatToFixed(params[1]);
-    c->lighting.lightModel.ambient.b = gglFloatToFixed(params[2]);
-    c->lighting.lightModel.ambient.a = gglFloatToFixed(params[3]);
-    invalidate_lighting(c);
-}
-
-void glLightModelxv(GLenum pname, const GLfixed *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (pname == GL_LIGHT_MODEL_TWO_SIDE) {
-        lightModelx(pname, params[0], c);
-        return;
-    }
-
-    if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    c->lighting.lightModel.ambient.r = params[0];
-    c->lighting.lightModel.ambient.g = params[1];
-    c->lighting.lightModel.ambient.b = params[2];
-    c->lighting.lightModel.ambient.a = params[3];
-    invalidate_lighting(c);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-void glLightf(GLenum i, GLenum pname, GLfloat param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    lightx(i, pname, gglFloatToFixed(param), c);
-}
-
-void glLightx(GLenum i, GLenum pname, GLfixed param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    lightx(i, pname, param, c);
-}
-
-void glLightfv(GLenum i, GLenum pname, const GLfloat *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    switch (pname) {
-    case GL_SPOT_EXPONENT:
-    case GL_SPOT_CUTOFF:
-    case GL_CONSTANT_ATTENUATION:
-    case GL_LINEAR_ATTENUATION:
-    case GL_QUADRATIC_ATTENUATION:
-        lightx(i, pname, gglFloatToFixed(params[0]), c);
-        return;
-    }
-
-    GLfixed paramsx[4];
-    paramsx[0] = gglFloatToFixed(params[0]);
-    paramsx[1] = gglFloatToFixed(params[1]);
-    paramsx[2] = gglFloatToFixed(params[2]);
-    if (pname != GL_SPOT_DIRECTION)
-        paramsx[3] = gglFloatToFixed(params[3]);
-
-    lightxv(i, pname, paramsx, c);
-}
-
-void glLightxv(GLenum i, GLenum pname, const GLfixed *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    lightxv(i, pname, params, c);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-void glMaterialf(GLenum face, GLenum pname, GLfloat param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    materialx(face, pname, gglFloatToFixed(param), c);
-}
-
-void glMaterialx(GLenum face, GLenum pname, GLfixed param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    materialx(face, pname, param, c);
-}
-
-void glMaterialfv(
-    GLenum face, GLenum pname, const GLfloat *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    GLfixed* what=0;
-    GLfixed* other=0;
-    switch (pname) {
-    case GL_AMBIENT:    what = c->lighting.front.ambient.v; break;
-    case GL_DIFFUSE:    what = c->lighting.front.diffuse.v; break;
-    case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
-    case GL_EMISSION:   what = c->lighting.front.emission.v; break;
-    case GL_AMBIENT_AND_DIFFUSE:
-        what  = c->lighting.front.ambient.v;
-        other = c->lighting.front.diffuse.v;
-        break;
-    case GL_SHININESS:
-        c->lighting.front.shininess = gglFloatToFixed(params[0]);
-        invalidate_lighting(c);
-        return;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    what[0] = gglFloatToFixed(params[0]);
-    what[1] = gglFloatToFixed(params[1]);
-    what[2] = gglFloatToFixed(params[2]);
-    what[3] = gglFloatToFixed(params[3]);
-    if (other) {
-        other[0] = what[0];
-        other[1] = what[1];
-        other[2] = what[2];
-        other[3] = what[3];
-    }
-    invalidate_lighting(c);
-}
-
-void glMaterialxv(
-    GLenum face, GLenum pname, const GLfixed *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (ggl_unlikely(face != GL_FRONT_AND_BACK)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    GLfixed* what=0;
-    GLfixed* other=0;
-    switch (pname) {
-    case GL_AMBIENT:    what = c->lighting.front.ambient.v; break;
-    case GL_DIFFUSE:    what = c->lighting.front.diffuse.v; break;
-    case GL_SPECULAR:   what = c->lighting.front.specular.v; break;
-    case GL_EMISSION:   what = c->lighting.front.emission.v; break;
-    case GL_AMBIENT_AND_DIFFUSE:
-        what  = c->lighting.front.ambient.v;
-        other = c->lighting.front.diffuse.v;
-        break;
-    case GL_SHININESS:
-        c->lighting.front.shininess = gglFloatToFixed(params[0]);
-        invalidate_lighting(c);
-        return;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    what[0] = params[0];
-    what[1] = params[1];
-    what[2] = params[2];
-    what[3] = params[3];
-    if (other) {
-        other[0] = what[0];
-        other[1] = what[1];
-        other[2] = what[2];
-        other[3] = what[3];
-    }
-    invalidate_lighting(c);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark fog
-#endif
-
-void glFogf(GLenum pname, GLfloat param) {
-    ogles_context_t* c = ogles_context_t::get();
-    GLfixed paramx = (GLfixed)param;
-    if (pname != GL_FOG_MODE)
-        paramx = gglFloatToFixed(param);
-    fogx(pname, paramx, c);
-}
-
-void glFogx(GLenum pname, GLfixed param) {
-    ogles_context_t* c = ogles_context_t::get();
-    fogx(pname, param, c);
-}
-
-void glFogfv(GLenum pname, const GLfloat *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (pname != GL_FOG_COLOR) {
-        GLfixed paramx = (GLfixed)params[0];
-        if (pname != GL_FOG_MODE)
-            paramx = gglFloatToFixed(params[0]);
-        fogx(pname, paramx, c);
-        return;
-    }
-    GLfixed paramsx[4];
-    paramsx[0] = gglFloatToFixed(params[0]);
-    paramsx[1] = gglFloatToFixed(params[1]);
-    paramsx[2] = gglFloatToFixed(params[2]);
-    paramsx[3] = gglFloatToFixed(params[3]);
-    c->rasterizer.procs.fogColor3xv(c, paramsx);
-}
-
-void glFogxv(GLenum pname, const GLfixed *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (pname != GL_FOG_COLOR) {
-        fogx(pname, params[0], c);
-        return;
-    }
-    c->rasterizer.procs.fogColor3xv(c, params);
-}
diff --git a/opengl/libagl/light.h b/opengl/libagl/light.h
deleted file mode 100644
index 39e3309..0000000
--- a/opengl/libagl/light.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* libs/opengles/light.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_LIGHT_H
-#define ANDROID_OPENGLES_LIGHT_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-
-// Set to 1 for object-space lighting evaluation.
-// There are still some bugs with object-space lighting,
-// especially visible in the San Angeles demo.
-#define OBJECT_SPACE_LIGHTING   0
-
-
-namespace android {
-
-namespace gl {
-struct ogles_context_t;
-};
-
-void ogles_init_light(ogles_context_t* c);
-void ogles_uninit_light(ogles_context_t* c);
-void ogles_invalidate_lighting_mvui(ogles_context_t* c);
-
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_LIGHT_H
-
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
deleted file mode 100644
index edd474d..0000000
--- a/opengl/libagl/matrix.cpp
+++ /dev/null
@@ -1,1123 +0,0 @@
-/* libs/opengles/matrix.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 <stdlib.h>
-#include <stdio.h>
-
-#include "context.h"
-#include "fp.h"
-#include "state.h"
-#include "matrix.h"
-#include "vertex.h"
-#include "light.h"
-
-#if defined(__arm__) && defined(__thumb__)
-#warning "matrix.cpp should not be compiled in thumb on ARM."
-#endif
-
-#define I(_i, _j) ((_j)+ 4*(_i))
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static const GLfloat gIdentityf[16] = { 1,0,0,0,
-                                        0,1,0,0,
-                                        0,0,1,0,
-                                        0,0,0,1 };
-
-static const matrixx_t gIdentityx = { 
-            {   0x10000,0,0,0,
-                0,0x10000,0,0,
-                0,0,0x10000,0,
-                0,0,0,0x10000
-            }
-        };
-
-static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o);
-static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o);
-static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o);
-static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void point3__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
-static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-void ogles_init_matrix(ogles_context_t* c)
-{
-    c->transforms.modelview.init(OGLES_MODELVIEW_STACK_DEPTH);
-    c->transforms.projection.init(OGLES_PROJECTION_STACK_DEPTH);
-    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
-        c->transforms.texture[i].init(OGLES_TEXTURE_STACK_DEPTH);
-
-    c->transforms.current = &c->transforms.modelview;
-    c->transforms.matrixMode = GL_MODELVIEW;
-    c->transforms.dirty =   transform_state_t::VIEWPORT | 
-                            transform_state_t::MVUI |
-                            transform_state_t::MVIT |
-                            transform_state_t::MVP;
-    c->transforms.mvp.loadIdentity();
-    c->transforms.mvp4.loadIdentity();
-    c->transforms.mvit4.loadIdentity();
-    c->transforms.mvui.loadIdentity();
-    c->transforms.vpt.loadIdentity();
-    c->transforms.vpt.zNear = 0.0f;
-    c->transforms.vpt.zFar  = 1.0f;
-}
-
-void ogles_uninit_matrix(ogles_context_t* c)
-{
-    c->transforms.modelview.uninit();
-    c->transforms.projection.uninit();
-    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
-        c->transforms.texture[i].uninit();
-}
-
-static void validate_perspective(ogles_context_t* c, vertex_t* v)
-{
-    const uint32_t enables = c->rasterizer.state.enables;
-    c->arrays.perspective = (c->clipPlanes.enable) ?
-        ogles_vertex_clipAllPerspective3D : ogles_vertex_perspective3D;
-    if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) {
-        c->arrays.perspective = ogles_vertex_perspective3DZ;
-        if (c->clipPlanes.enable || (enables&GGL_ENABLE_FOG))
-            c->arrays.perspective = ogles_vertex_clipAllPerspective3DZ;
-    }
-    if ((c->arrays.vertex.size != 4) &&
-        (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)) {
-        c->arrays.perspective = ogles_vertex_perspective2D;
-    }
-    c->arrays.perspective(c, v);
-}
-
-void ogles_invalidate_perspective(ogles_context_t* c)
-{
-    c->arrays.perspective = validate_perspective;
-}
-
-void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want)
-{
-    int dirty = c->transforms.dirty & want;
-
-    // Validate the modelview
-    if (dirty & transform_state_t::MODELVIEW) {
-        c->transforms.modelview.validate();
-    }
-
-    // Validate the projection stack (in fact, it's never needed)
-    if (dirty & transform_state_t::PROJECTION) {
-        c->transforms.projection.validate();
-    }
-
-    // Validate the viewport transformation
-    if (dirty & transform_state_t::VIEWPORT) {
-        vp_transform_t& vpt = c->transforms.vpt;
-        vpt.transform.matrix.load(vpt.matrix);
-        vpt.transform.picker();
-    }
-
-    // We need to update the mvp (used to transform each vertex)
-    if (dirty & transform_state_t::MVP) {
-        c->transforms.update_mvp();
-        // invalidate perspective (divide by W) and view volume clipping
-        ogles_invalidate_perspective(c);
-    }
-
-    // Validate the mvui (for normal transformation)
-    if (dirty & transform_state_t::MVUI) {
-        c->transforms.update_mvui();
-        ogles_invalidate_lighting_mvui(c);
-    }
-
-    // Validate the texture stack
-    if (dirty & transform_state_t::TEXTURE) {
-        for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++)
-            c->transforms.texture[i].validate();
-    }
-
-    // Validate the mvit4 (user-clip planes)
-    if (dirty & transform_state_t::MVIT) {
-        c->transforms.update_mvit();
-    }
-
-    c->transforms.dirty &= ~want;
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark transform_t
-#endif
-
-void transform_t::loadIdentity() {
-    matrix = gIdentityx;
-    flags = 0;
-    ops = OP_IDENTITY;
-    point2 = point2__nop;
-    point3 = point3__nop;
-    point4 = point4__nop;
-}
-
-
-static inline
-int notZero(GLfixed v) {
-    return abs(v) & ~0x3;
-}
-
-static inline
-int notOne(GLfixed v) {
-    return notZero(v - 0x10000);
-}
-
-void transform_t::picker()
-{
-    const GLfixed* const m = matrix.m;
-
-    // XXX: picker needs to be smarter
-    flags = 0;
-    ops = OP_ALL;
-    point2 = point2__generic;
-    point3 = point3__generic;
-    point4 = point4__generic;
-    
-    // find out if this is a 2D projection
-    if (!(notZero(m[3]) | notZero(m[7]) | notZero(m[11]) | notOne(m[15]))) {
-        flags |= FLAGS_2D_PROJECTION;
-    }
-}
-
-void mvui_transform_t::picker()
-{
-    flags = 0;
-    ops = OP_ALL;
-    point3 = point3__mvui;
-    point4 = point4__mvui;
-}
-
-void transform_t::dump(const char* what)
-{
-    GLfixed const * const m = matrix.m;
-    ALOGD("%s:", what);
-    for (int i=0 ; i<4 ; i++)
-        ALOGD("[%08x %08x %08x %08x] [%f %f %f %f]\n",
-            m[I(0,i)], m[I(1,i)], m[I(2,i)], m[I(3,i)],
-            fixedToFloat(m[I(0,i)]),
-            fixedToFloat(m[I(1,i)]), 
-            fixedToFloat(m[I(2,i)]),
-            fixedToFloat(m[I(3,i)]));
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark matrixx_t
-#endif
-
-void matrixx_t::load(const matrixf_t& rhs) {
-    GLfixed* xp = m;
-    GLfloat const* fp = rhs.elements();
-    unsigned int i = 16;
-    do {
-        const GLfloat f = *fp++;
-        *xp++ = isZerof(f) ? 0 : gglFloatToFixed(f);
-    } while (--i);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark matrixf_t
-#endif
-
-void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rhs)
-{
-    GLfloat const* const m = lhs.m;
-    for (int i=0 ; i<4 ; i++) {
-        const float rhs_i0 = rhs.m[ I(i,0) ];
-        float ri0 = m[ I(0,0) ] * rhs_i0;
-        float ri1 = m[ I(0,1) ] * rhs_i0;
-        float ri2 = m[ I(0,2) ] * rhs_i0;
-        float ri3 = m[ I(0,3) ] * rhs_i0;
-        for (int j=1 ; j<4 ; j++) {
-            const float rhs_ij = rhs.m[ I(i,j) ];
-            ri0 += m[ I(j,0) ] * rhs_ij;
-            ri1 += m[ I(j,1) ] * rhs_ij;
-            ri2 += m[ I(j,2) ] * rhs_ij;
-            ri3 += m[ I(j,3) ] * rhs_ij;
-        }
-        r.m[ I(i,0) ] = ri0;
-        r.m[ I(i,1) ] = ri1;
-        r.m[ I(i,2) ] = ri2;
-        r.m[ I(i,3) ] = ri3;
-    }
-}
-
-void matrixf_t::dump(const char* what) {
-    ALOGD("%s", what);
-    ALOGD("[ %9f %9f %9f %9f ]", m[I(0,0)], m[I(1,0)], m[I(2,0)], m[I(3,0)]);
-    ALOGD("[ %9f %9f %9f %9f ]", m[I(0,1)], m[I(1,1)], m[I(2,1)], m[I(3,1)]);
-    ALOGD("[ %9f %9f %9f %9f ]", m[I(0,2)], m[I(1,2)], m[I(2,2)], m[I(3,2)]);
-    ALOGD("[ %9f %9f %9f %9f ]", m[I(0,3)], m[I(1,3)], m[I(2,3)], m[I(3,3)]);
-}
-
-void matrixf_t::loadIdentity() {
-    memcpy(m, gIdentityf, sizeof(m));
-}
-
-void matrixf_t::set(const GLfixed* rhs) {
-    load(rhs);
-}
-
-void matrixf_t::set(const GLfloat* rhs) {
-    load(rhs);
-}
-
-void matrixf_t::load(const GLfixed* rhs) {
-    GLfloat* fp = m;
-    unsigned int i = 16;
-    do {
-        *fp++ = fixedToFloat(*rhs++);
-    } while (--i);
-}
-
-void matrixf_t::load(const GLfloat* rhs) {
-    memcpy(m, rhs, sizeof(m));
-}
-
-void matrixf_t::load(const matrixf_t& rhs) {
-    operator = (rhs);
-}
-
-void matrixf_t::multiply(const matrixf_t& rhs) {
-    matrixf_t r;
-    multiply(r, *this, rhs);
-    operator = (r);
-}
-
-void matrixf_t::translate(GLfloat x, GLfloat y, GLfloat z) {
-    for (int i=0 ; i<4 ; i++) {
-        m[12+i] += m[i]*x + m[4+i]*y + m[8+i]*z;
-    }
-}
-
-void matrixf_t::scale(GLfloat x, GLfloat y, GLfloat z) {
-    for (int i=0 ; i<4 ; i++) {
-        m[  i] *= x;
-        m[4+i] *= y;
-        m[8+i] *= z;
-    }
-}
-
-void matrixf_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
-{
-    matrixf_t rotation;
-    GLfloat* r = rotation.m;
-    GLfloat c, s;
-    r[3] = 0;   r[7] = 0;   r[11]= 0;
-    r[12]= 0;   r[13]= 0;   r[14]= 0;   r[15]= 1;
-    a *= GLfloat(M_PI / 180.0f);
-    sincosf(a, &s, &c);
-    if (isOnef(x) && isZerof(y) && isZerof(z)) {
-        r[5] = c;   r[10]= c;
-        r[6] = s;   r[9] = -s;
-        r[1] = 0;   r[2] = 0;
-        r[4] = 0;   r[8] = 0;
-        r[0] = 1;
-    } else if (isZerof(x) && isOnef(y) && isZerof(z)) {
-        r[0] = c;   r[10]= c;
-        r[8] = s;   r[2] = -s;
-        r[1] = 0;   r[4] = 0;
-        r[6] = 0;   r[9] = 0;
-        r[5] = 1;
-    } else if (isZerof(x) && isZerof(y) && isOnef(z)) {
-        r[0] = c;   r[5] = c;
-        r[1] = s;   r[4] = -s;
-        r[2] = 0;   r[6] = 0;
-        r[8] = 0;   r[9] = 0;
-        r[10]= 1;
-    } else {
-        const GLfloat len = sqrtf(x*x + y*y + z*z);
-        if (!isOnef(len)) {
-            const GLfloat recipLen = reciprocalf(len);
-            x *= recipLen;
-            y *= recipLen;
-            z *= recipLen;
-        }
-        const GLfloat nc = 1.0f - c;
-        const GLfloat xy = x * y;
-        const GLfloat yz = y * z;
-        const GLfloat zx = z * x;
-        const GLfloat xs = x * s;
-        const GLfloat ys = y * s;
-        const GLfloat zs = z * s;		
-        r[ 0] = x*x*nc +  c;    r[ 4] =  xy*nc - zs;    r[ 8] =  zx*nc + ys;
-        r[ 1] =  xy*nc + zs;    r[ 5] = y*y*nc +  c;    r[ 9] =  yz*nc - xs;
-        r[ 2] =  zx*nc - ys;    r[ 6] =  yz*nc + xs;    r[10] = z*z*nc +  c;
-    }
-    multiply(rotation);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark matrix_stack_t
-#endif
-
-void matrix_stack_t::init(int depth) {
-    stack = new matrixf_t[depth];
-    ops = new uint8_t[depth];
-    maxDepth = depth;
-    depth = 0;
-    dirty = 0;
-    loadIdentity();
-}
-
-void matrix_stack_t::uninit() {
-    delete [] stack;
-    delete [] ops;
-}
-
-void matrix_stack_t::loadIdentity() {
-    transform.loadIdentity();
-    stack[depth].loadIdentity();
-    ops[depth] = OP_IDENTITY;
-}
-
-void matrix_stack_t::load(const GLfixed* rhs)
-{   
-    memcpy(transform.matrix.m, rhs, sizeof(transform.matrix.m));
-    stack[depth].load(rhs);
-    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
-}
-
-void matrix_stack_t::load(const GLfloat* rhs)
-{
-    stack[depth].load(rhs);
-    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
-}
-
-void matrix_stack_t::multiply(const matrixf_t& rhs)
-{    
-    stack[depth].multiply(rhs);
-    ops[depth] = OP_ALL;    // TODO: we should look at the matrix
-}
-
-void matrix_stack_t::translate(GLfloat x, GLfloat y, GLfloat z)
-{
-    stack[depth].translate(x,y,z);
-    ops[depth] |= OP_TRANSLATE;
-}
-
-void matrix_stack_t::scale(GLfloat x, GLfloat y, GLfloat z)
-{
-    stack[depth].scale(x,y,z);
-    if (x==y && y==z) {
-        ops[depth] |= OP_UNIFORM_SCALE;
-    } else {
-        ops[depth] |= OP_SCALE;
-    }
-}
-
-void matrix_stack_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
-{
-    stack[depth].rotate(a,x,y,z);
-    ops[depth] |= OP_ROTATE;
-}
-
-void matrix_stack_t::validate()
-{
-    if (dirty & DO_FLOAT_TO_FIXED) {
-        transform.matrix.load(top());
-    }
-    if (dirty & DO_PICKER) {
-        transform.picker();
-    }
-    dirty = 0;
-}
-
-GLint matrix_stack_t::push()
-{
-    if (depth >= (maxDepth-1)) {
-        return GL_STACK_OVERFLOW;
-    }
-    stack[depth+1] = stack[depth];
-    ops[depth+1] = ops[depth];
-    depth++;
-    return 0;
-}
-
-GLint matrix_stack_t::pop()
-{
-    if (depth == 0) {
-        return GL_STACK_UNDERFLOW;
-    }
-    depth--;
-    return 0;
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark vp_transform_t
-#endif
-
-void vp_transform_t::loadIdentity() {
-    transform.loadIdentity();
-    matrix.loadIdentity();
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark transform_state_t
-#endif
-
-void transform_state_t::invalidate()
-{
-    switch (matrixMode) {
-    case GL_MODELVIEW:  dirty |= MODELVIEW  | MVP | MVUI | MVIT;    break;
-    case GL_PROJECTION: dirty |= PROJECTION | MVP;                  break;
-    case GL_TEXTURE:    dirty |= TEXTURE    | MVP;                  break;
-    }
-    current->dirty =    matrix_stack_t::DO_PICKER |
-                        matrix_stack_t::DO_FLOAT_TO_FIXED;
-}
-
-void transform_state_t::update_mvp()
-{
-    matrixf_t temp_mvp;
-    matrixf_t::multiply(temp_mvp, projection.top(), modelview.top());
-    mvp4.matrix.load(temp_mvp);
-    mvp4.picker();
-
-    if (mvp4.flags & transform_t::FLAGS_2D_PROJECTION) {
-        // the mvp matrix doesn't transform W, in this case we can
-        // premultiply it with the viewport transformation. In addition to
-        // being more efficient, this is also much more accurate and in fact
-        // is needed for 2D drawing with a resulting 1:1 mapping.
-        matrixf_t mvpv;
-        matrixf_t::multiply(mvpv, vpt.matrix, temp_mvp);
-        mvp.matrix.load(mvpv);
-        mvp.picker();
-    } else {
-        mvp = mvp4;
-    }
-}
-
-static __attribute__((noinline))
-void invert(GLfloat* inverse, const GLfloat* src)
-{
-    double t;
-    int i, j, k, swap;
-    GLfloat tmp[4][4];
-    
-    memcpy(inverse, gIdentityf, sizeof(gIdentityf));
-    memcpy(tmp, src, sizeof(GLfloat)*16);
-    
-    for (i = 0; i < 4; i++) {
-        // look for largest element in column
-        swap = i;
-        for (j = i + 1; j < 4; j++) {
-            if (fabs(tmp[j][i]) > fabs(tmp[i][i])) {
-                swap = j;
-            }
-        }
-        
-        if (swap != i) {
-            /* swap rows. */
-            for (k = 0; k < 4; k++) {
-                t = tmp[i][k];
-                tmp[i][k] = tmp[swap][k];
-                tmp[swap][k] = t;
-                
-                t = inverse[i*4+k];
-                inverse[i*4+k] = inverse[swap*4+k];
-                inverse[swap*4+k] = t;
-            }
-        }
-        
-        t = 1.0f / tmp[i][i];
-        for (k = 0; k < 4; k++) {
-            tmp[i][k] *= t;
-            inverse[i*4+k] *= t;
-        }
-        for (j = 0; j < 4; j++) {
-            if (j != i) {
-                t = tmp[j][i];
-                for (k = 0; k < 4; k++) {
-                    tmp[j][k] -= tmp[i][k]*t;
-                    inverse[j*4+k] -= inverse[i*4+k]*t;
-                }
-            }
-        }
-    }
-}
-
-void transform_state_t::update_mvit()
-{
-    GLfloat r[16];
-    const GLfloat* const mv = modelview.top().elements();
-    invert(r, mv);
-    // convert to fixed-point and transpose
-    GLfixed* const x = mvit4.matrix.m;
-    for (int i=0 ; i<4 ; i++)
-        for (int j=0 ; j<4 ; j++)
-            x[I(i,j)] = gglFloatToFixed(r[I(j,i)]);
-    mvit4.picker();
-}
-
-void transform_state_t::update_mvui()
-{
-    GLfloat r[16];
-    const GLfloat* const mv = modelview.top().elements();
-    
-    /*
-    When evaluating the lighting equation in eye-space, normals
-    are transformed by the upper 3x3 modelview inverse-transpose.
-    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
-
-    (note that inverse-transpose is distributive).
-    Also note that:
-        l(obj) = inv(modelview).l(eye) for local light
-        l(obj) =  tr(modelview).l(eye) for infinite light
-    */
-
-    invert(r, mv);
-
-    GLfixed* const x = mvui.matrix.m;
-
-#if OBJECT_SPACE_LIGHTING
-    for (int i=0 ; i<4 ; i++)
-        for (int j=0 ; j<4 ; j++)
-            x[I(i,j)] = gglFloatToFixed(r[I(i,j)]);
-#else
-    for (int i=0 ; i<4 ; i++)
-        for (int j=0 ; j<4 ; j++)
-            x[I(i,j)] = gglFloatToFixed(r[I(j,i)]);
-#endif
-
-    mvui.picker();
-}
-
-
-// ----------------------------------------------------------------------------
-// transformation and matrices API
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark transformation and matrices API
-#endif
-
-int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y)
-{
-    c->viewport.surfaceport.x = x;
-    c->viewport.surfaceport.y = y;
-
-    ogles_viewport(c, 
-            c->viewport.x,
-            c->viewport.y,
-            c->viewport.w,
-            c->viewport.h);
-
-    ogles_scissor(c,
-            c->viewport.scissor.x,
-            c->viewport.scissor.y,
-            c->viewport.scissor.w,
-            c->viewport.scissor.h);
-
-    return 0;
-}
-
-void ogles_scissor(ogles_context_t* c, 
-        GLint x, GLint y, GLsizei w, GLsizei h)
-{
-    if ((w|h) < 0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    c->viewport.scissor.x = x;
-    c->viewport.scissor.y = y;
-    c->viewport.scissor.w = w;
-    c->viewport.scissor.h = h;
-    
-    x += c->viewport.surfaceport.x;
-    y += c->viewport.surfaceport.y;
-
-    y = c->rasterizer.state.buffers.color.height - (y + h);
-    c->rasterizer.procs.scissor(c, x, y, w, h);
-}
-
-void ogles_viewport(ogles_context_t* c,
-        GLint x, GLint y, GLsizei w, GLsizei h)
-{
-    if ((w|h)<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    c->viewport.x = x;
-    c->viewport.y = y;
-    c->viewport.w = w;
-    c->viewport.h = h;
-
-    x += c->viewport.surfaceport.x;
-    y += c->viewport.surfaceport.y;
-
-    GLint H = c->rasterizer.state.buffers.color.height;
-    GLfloat sx = div2f(w);
-    GLfloat ox = sx + x;
-    GLfloat sy = div2f(h);
-    GLfloat oy = sy - y + (H - h);
-
-    GLfloat near = c->transforms.vpt.zNear;
-    GLfloat far  = c->transforms.vpt.zFar;
-    GLfloat A = div2f(far - near);
-    GLfloat B = div2f(far + near);
-
-    // compute viewport matrix
-    GLfloat* const f = c->transforms.vpt.matrix.editElements();
-    f[0] = sx;  f[4] = 0;   f[ 8] = 0;  f[12] = ox;
-    f[1] = 0;   f[5] =-sy;  f[ 9] = 0;  f[13] = oy;
-    f[2] = 0;   f[6] = 0;   f[10] = A;  f[14] = B;
-    f[3] = 0;   f[7] = 0;   f[11] = 0;  f[15] = 1;
-    c->transforms.dirty |= transform_state_t::VIEWPORT;
-    if (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)
-        c->transforms.dirty |= transform_state_t::MVP;
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark matrix * vertex
-#endif
-
-void point2__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
-    const GLfixed* const m = mx->matrix.m;
-    const GLfixed rx = rhs->x;
-    const GLfixed ry = rhs->y;
-    lhs->x = mla2a(rx, m[ 0], ry, m[ 4], m[12]); 
-    lhs->y = mla2a(rx, m[ 1], ry, m[ 5], m[13]);
-    lhs->z = mla2a(rx, m[ 2], ry, m[ 6], m[14]);
-    lhs->w = mla2a(rx, m[ 3], ry, m[ 7], m[15]);
-}
-
-void point3__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
-    const GLfixed* const m = mx->matrix.m;
-    const GLfixed rx = rhs->x;
-    const GLfixed ry = rhs->y;
-    const GLfixed rz = rhs->z;
-    lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); 
-    lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
-    lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
-    lhs->w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]);
-}
-
-void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
-    const GLfixed* const m = mx->matrix.m;
-    const GLfixed rx = rhs->x;
-    const GLfixed ry = rhs->y;
-    const GLfixed rz = rhs->z;
-    const GLfixed rw = rhs->w;
-    lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]); 
-    lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]);
-    lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]);
-    lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
-}
-
-void point3__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
-    // this is used for transforming light positions back to object space.
-    // w is used as a switch for directional lights, so we need
-    // to preserve it.
-    const GLfixed* const m = mx->matrix.m;
-    const GLfixed rx = rhs->x;
-    const GLfixed ry = rhs->y;
-    const GLfixed rz = rhs->z;
-    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]);
-    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
-    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
-    lhs->w = 0;
-}
-
-void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
-    // this is used for transforming light positions back to object space.
-    // w is used as a switch for directional lights, so we need
-    // to preserve it.
-    const GLfixed* const m = mx->matrix.m;
-    const GLfixed rx = rhs->x;
-    const GLfixed ry = rhs->y;
-    const GLfixed rz = rhs->z;
-    const GLfixed rw = rhs->w;
-    lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]);
-    lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]);
-    lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]);
-    lhs->w = rw;
-}
-
-void point2__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
-    lhs->z = 0;
-    lhs->w = 0x10000;
-    if (lhs != rhs) {
-        lhs->x = rhs->x;
-        lhs->y = rhs->y;
-    }
-}
-
-void point3__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
-    lhs->w = 0x10000;
-    if (lhs != rhs) {
-        lhs->x = rhs->x;
-        lhs->y = rhs->y;
-        lhs->z = rhs->z;
-    }
-}
-
-void point4__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) {
-    if (lhs != rhs)
-        *lhs = *rhs;
-}
-
-
-static void frustumf(
-            GLfloat left, GLfloat right, 
-            GLfloat bottom, GLfloat top,
-            GLfloat zNear, GLfloat zFar,
-            ogles_context_t* c)
-    {
-    if (cmpf(left,right) ||
-        cmpf(top, bottom) ||
-        cmpf(zNear, zFar) ||
-        isZeroOrNegativef(zNear) ||
-        isZeroOrNegativef(zFar))
-    {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    const GLfloat r_width  = reciprocalf(right - left);
-    const GLfloat r_height = reciprocalf(top - bottom);
-    const GLfloat r_depth  = reciprocalf(zNear - zFar);
-    const GLfloat x = mul2f(zNear * r_width);
-    const GLfloat y = mul2f(zNear * r_height);
-    const GLfloat A = mul2f((right + left) * r_width);
-    const GLfloat B = (top + bottom) * r_height;
-    const GLfloat C = (zFar + zNear) * r_depth;
-    const GLfloat D = mul2f(zFar * zNear * r_depth);
-    GLfloat f[16];
-    f[ 0] = x;
-    f[ 5] = y;
-    f[ 8] = A;
-    f[ 9] = B;
-    f[10] = C;
-    f[14] = D;
-    f[11] = -1.0f;
-    f[ 1] = f[ 2] = f[ 3] =
-    f[ 4] = f[ 6] = f[ 7] =
-    f[12] = f[13] = f[15] = 0.0f;
-
-    matrixf_t rhs;
-    rhs.set(f);
-    c->transforms.current->multiply(rhs);
-    c->transforms.invalidate();
-}
-
-static void orthof( 
-        GLfloat left, GLfloat right, 
-        GLfloat bottom, GLfloat top,
-        GLfloat zNear, GLfloat zFar,
-        ogles_context_t* c)
-{
-    if (cmpf(left,right) ||
-        cmpf(top, bottom) ||
-        cmpf(zNear, zFar))
-    {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    const GLfloat r_width  = reciprocalf(right - left);
-    const GLfloat r_height = reciprocalf(top - bottom);
-    const GLfloat r_depth  = reciprocalf(zFar - zNear);
-    const GLfloat x =  mul2f(r_width);
-    const GLfloat y =  mul2f(r_height);
-    const GLfloat z = -mul2f(r_depth);
-    const GLfloat tx = -(right + left) * r_width;
-    const GLfloat ty = -(top + bottom) * r_height;
-    const GLfloat tz = -(zFar + zNear) * r_depth;
-    GLfloat f[16];
-    f[ 0] = x;
-    f[ 5] = y;
-    f[10] = z;
-    f[12] = tx;
-    f[13] = ty;
-    f[14] = tz;
-    f[15] = 1.0f;
-    f[ 1] = f[ 2] = f[ 3] =
-    f[ 4] = f[ 6] = f[ 7] =
-    f[ 8] = f[ 9] = f[11] = 0.0f;
-    matrixf_t rhs;
-    rhs.set(f);
-    c->transforms.current->multiply(rhs);
-    c->transforms.invalidate();
-}
-
-static void depthRangef(GLclampf zNear, GLclampf zFar, ogles_context_t* c)
-{
-    zNear = clampToZerof(zNear > 1 ? 1 : zNear);
-    zFar  = clampToZerof(zFar  > 1 ? 1 : zFar);
-    GLfloat* const f = c->transforms.vpt.matrix.editElements();
-    f[10] = div2f(zFar - zNear);
-    f[14] = div2f(zFar + zNear);
-    c->transforms.dirty |= transform_state_t::VIEWPORT;
-    c->transforms.vpt.zNear = zNear;
-    c->transforms.vpt.zFar  = zFar;
-}
-
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-using namespace android;
-
-void glMatrixMode(GLenum mode)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    matrix_stack_t* stack = 0;
-    switch (mode) {
-    case GL_MODELVIEW:
-        stack = &c->transforms.modelview;
-        break;
-    case GL_PROJECTION:
-        stack = &c->transforms.projection;
-        break;
-    case GL_TEXTURE:
-        stack = &c->transforms.texture[c->textures.active];
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->transforms.matrixMode = mode;
-    c->transforms.current = stack;
-}
-
-void glLoadIdentity()
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->loadIdentity(); // also loads the GLfixed transform
-    c->transforms.invalidate();
-    c->transforms.current->dirty = 0;
-}
-
-void glLoadMatrixf(const GLfloat* m)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->load(m);
-    c->transforms.invalidate();
-}
-
-void glLoadMatrixx(const GLfixed* m)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->load(m); // also loads the GLfixed transform
-    c->transforms.invalidate();
-    c->transforms.current->dirty &= ~matrix_stack_t::DO_FLOAT_TO_FIXED;
-}
-
-void glMultMatrixf(const GLfloat* m)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    matrixf_t rhs;
-    rhs.set(m);
-    c->transforms.current->multiply(rhs);
-    c->transforms.invalidate();
-}
-
-void glMultMatrixx(const GLfixed* m)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    matrixf_t rhs;
-    rhs.set(m);
-    c->transforms.current->multiply(rhs);
-    c->transforms.invalidate();
-}
-
-void glPopMatrix()
-{
-    ogles_context_t* c = ogles_context_t::get();
-    GLint err = c->transforms.current->pop();
-    if (ggl_unlikely(err)) {
-        ogles_error(c, err);
-        return;
-    }
-    c->transforms.invalidate();
-}
-
-void glPushMatrix()
-{
-    ogles_context_t* c = ogles_context_t::get();
-    GLint err = c->transforms.current->push();
-    if (ggl_unlikely(err)) {
-        ogles_error(c, err);
-        return;
-    }
-    c->transforms.invalidate();
-}
-
-void glFrustumf(
-        GLfloat left, GLfloat right, 
-        GLfloat bottom, GLfloat top,
-        GLfloat zNear, GLfloat zFar)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    frustumf(left, right, bottom, top, zNear, zFar, c);
-}
-
-void glFrustumx( 
-        GLfixed left, GLfixed right,
-        GLfixed bottom, GLfixed top,
-        GLfixed zNear, GLfixed zFar)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    frustumf( fixedToFloat(left), fixedToFloat(right),
-              fixedToFloat(bottom), fixedToFloat(top),
-              fixedToFloat(zNear), fixedToFloat(zFar),
-              c);
-}
-
-void glOrthof( 
-        GLfloat left, GLfloat right, 
-        GLfloat bottom, GLfloat top,
-        GLfloat zNear, GLfloat zFar)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    orthof(left, right, bottom, top, zNear, zFar, c);
-}
-
-void glOrthox(
-        GLfixed left, GLfixed right,
-        GLfixed bottom, GLfixed top,
-        GLfixed zNear, GLfixed zFar)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    orthof( fixedToFloat(left), fixedToFloat(right),
-            fixedToFloat(bottom), fixedToFloat(top),
-            fixedToFloat(zNear), fixedToFloat(zFar),
-            c);
-}
-
-void glRotatef(GLfloat a, GLfloat x, GLfloat y, GLfloat z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->rotate(a, x, y, z);
-    c->transforms.invalidate();
-}
-
-void glRotatex(GLfixed a, GLfixed x, GLfixed y, GLfixed z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->rotate( 
-            fixedToFloat(a), fixedToFloat(x),
-            fixedToFloat(y), fixedToFloat(z));
-    c->transforms.invalidate();
-}
-
-void glScalef(GLfloat x, GLfloat y, GLfloat z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->scale(x, y, z);
-    c->transforms.invalidate();
-}
-
-void glScalex(GLfixed x, GLfixed y, GLfixed z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->scale(
-            fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
-    c->transforms.invalidate();
-}
-
-void glTranslatef(GLfloat x, GLfloat y, GLfloat z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->translate(x, y, z);
-    c->transforms.invalidate();
-}
-
-void glTranslatex(GLfixed x, GLfixed y, GLfixed z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->transforms.current->translate(
-            fixedToFloat(x), fixedToFloat(y), fixedToFloat(z));
-    c->transforms.invalidate();
-}
-
-void glScissor(GLint x, GLint y, GLsizei w, GLsizei h)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    ogles_scissor(c, x, y, w, h);
-}
-
-void glViewport(GLint x, GLint y, GLsizei w, GLsizei h)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    ogles_viewport(c, x, y, w, h);
-}
-
-void glDepthRangef(GLclampf zNear, GLclampf zFar)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    depthRangef(zNear, zFar, c);
-}
-
-void glDepthRangex(GLclampx zNear, GLclampx zFar)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    depthRangef(fixedToFloat(zNear), fixedToFloat(zFar), c);
-}
-
-void glPolygonOffsetx(GLfixed factor, GLfixed units)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->polygonOffset.factor = factor;
-    c->polygonOffset.units = units;
-}
-
-void glPolygonOffset(GLfloat factor, GLfloat units)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->polygonOffset.factor = gglFloatToFixed(factor);
-    c->polygonOffset.units = gglFloatToFixed(units);
-}
-
-GLbitfield glQueryMatrixxOES(GLfixed* m, GLint* e)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    GLbitfield status = 0;
-    GLfloat const* f = c->transforms.current->top().elements();
-    for  (int i=0 ; i<16 ; i++) {
-        if (isnan(f[i]) || isinf(f[i])) {
-            status |= 1<<i;
-            continue;
-        }
-        e[i] = exponent(f[i]) - 7;
-        m[i] = mantissa(f[i]);
-    }
-    return status;
-}
diff --git a/opengl/libagl/matrix.h b/opengl/libagl/matrix.h
deleted file mode 100644
index cafc119..0000000
--- a/opengl/libagl/matrix.h
+++ /dev/null
@@ -1,399 +0,0 @@
-/* libs/opengles/matrix.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_MATRIX_H
-#define ANDROID_OPENGLES_MATRIX_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-#include <utils/Log.h>
-
-#include <private/pixelflinger/ggl_context.h>
-
-#include <GLES/gl.h>
-
-namespace android {
-
-const int OGLES_MODELVIEW_STACK_DEPTH   = 16;
-const int OGLES_PROJECTION_STACK_DEPTH  =  2;
-const int OGLES_TEXTURE_STACK_DEPTH     =  2;
-
-void ogles_init_matrix(ogles_context_t*);
-void ogles_uninit_matrix(ogles_context_t*);
-void ogles_invalidate_perspective(ogles_context_t* c);
-void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want);
-
-int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y);
-
-void ogles_scissor(ogles_context_t* c, 
-        GLint x, GLint y, GLsizei w, GLsizei h);
-
-void ogles_viewport(ogles_context_t* c,
-        GLint x, GLint y, GLsizei w, GLsizei h);
-
-inline void ogles_validate_transform(
-        ogles_context_t* c, uint32_t want)
-{
-    if (c->transforms.dirty & want)
-        ogles_validate_transform_impl(c, want);
-}
-
-// ----------------------------------------------------------------------------
-
-inline
-GLfixed vsquare3(GLfixed a, GLfixed b, GLfixed c) 
-{
-#if defined(__arm__) && !defined(__thumb__)
-
-    GLfixed r;
-    int32_t t;
-    asm(
-        "smull %0, %1, %2, %2       \n"
-        "smlal %0, %1, %3, %3       \n"
-        "smlal %0, %1, %4, %4       \n"
-        "movs  %0, %0, lsr #16      \n"
-        "adc   %0, %0, %1, lsl #16  \n"
-        :   "=&r"(r), "=&r"(t) 
-        :   "%r"(a), "r"(b), "r"(c)
-        :   "cc"
-        ); 
-    return r;
-
-#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
-
-    GLfixed res;
-    int32_t t1,t2,t3;
-    asm(
-        "mult  %[a], %[a]       \r\n"
-        "li    %[res],0x8000 \r\n"
-        "madd   %[b],%[b] \r\n"
-        "move   %[t3],$zero \r\n"
-        "madd   %[c],%[c] \r\n"
-        "mflo   %[t1]\r\n"
-        "mfhi   %[t2]\r\n"
-        "addu   %[t1],%[res],%[t1]\r\n"          /*add 0x8000*/
-        "sltu   %[t3],%[t1],%[res]\r\n"
-        "addu   %[t2],%[t2],%[t3]\r\n"
-        "srl    %[res],%[t1],16\r\n"
-        "sll    %[t2],%[t2],16\r\n"
-        "or     %[res],%[res],%[t2]\r\n"
-        :   [res]"=&r"(res),[t1]"=&r"(t1),[t2]"=&r"(t2),[t3]"=&r"(t3)
-        :   [a] "r" (a),[b] "r" (b),[c] "r" (c)
-        : "%hi","%lo"
-        );
-    return res;
-
-#else
-
-    return ((   int64_t(a)*a +
-                int64_t(b)*b +
-                int64_t(c)*c + 0x8000)>>16);
-
-#endif
-}
-
-static inline GLfixed mla2a( GLfixed a0, GLfixed b0,
-                            GLfixed a1, GLfixed b1,
-                            GLfixed c)
-{
-#if defined(__arm__) && !defined(__thumb__)
-                            
-    GLfixed r;
-    int32_t t;
-    asm(
-        "smull %0, %1, %2, %3       \n"
-        "smlal %0, %1, %4, %5       \n"
-        "add   %0, %6, %0, lsr #16  \n"
-        "add   %0, %0, %1, lsl #16  \n"
-        :   "=&r"(r), "=&r"(t) 
-        :   "%r"(a0), "r"(b0), 
-            "%r"(a1), "r"(b1),
-            "r"(c)
-        :
-        ); 
-    return r;
-    
-#else
-
-    return ((   int64_t(a0)*b0 +
-                int64_t(a1)*b1)>>16) + c;
-
-#endif
-}
-
-static inline GLfixed mla3a( GLfixed a0, GLfixed b0,
-                             GLfixed a1, GLfixed b1,
-                             GLfixed a2, GLfixed b2,
-                             GLfixed c)
-{
-#if defined(__arm__) && !defined(__thumb__)
-                            
-    GLfixed r;
-    int32_t t;
-    asm(
-        "smull %0, %1, %2, %3       \n"
-        "smlal %0, %1, %4, %5       \n"
-        "smlal %0, %1, %6, %7       \n"
-        "add   %0, %8, %0, lsr #16  \n"
-        "add   %0, %0, %1, lsl #16  \n"
-        :   "=&r"(r), "=&r"(t) 
-        :   "%r"(a0), "r"(b0),
-            "%r"(a1), "r"(b1),
-            "%r"(a2), "r"(b2),
-            "r"(c)
-        :
-        ); 
-    return r;
-    
-#elif defined(__mips__)  && !defined(__LP64__) && __mips_isa_rev < 6
-
-    GLfixed res;
-    int32_t t1,t2;
-    asm(
-        "mult  %[a0],%[b0]       \r\n"
-        "madd  %[a1],%[b1]       \r\n"
-        "madd  %[a2],%[b2]       \r\n"
-        "mflo  %[t2]\r\n"
-        "mfhi  %[t1]\r\n"
-        "srl    %[t2],%[t2],16\r\n"
-        "sll    %[t1],%[t1],16\r\n"
-        "or     %[t2],%[t2],%[t1]\r\n"
-        "addu   %[res],%[t2],%[c]"
-        :   [res]"=&r"(res),[t1]"=&r"(t1),[t2]"=&r"(t2)
-        :   [a0] "r" (a0),[b0] "r" (b0),[a1] "r" (a1),[b1] "r" (b1),[a2] "r" (a2),[b2] "r" (b2),[c] "r" (c)
-        : "%hi","%lo"
-        );
-    return res;
-
-#else
-
-    return ((   int64_t(a0)*b0 +
-                int64_t(a1)*b1 +
-                int64_t(a2)*b2)>>16) + c;
-
-#endif
-}
-
-// b0, b1, b2 are signed 16-bit quanities
-// that have been shifted right by 'shift' bits relative to normal
-// S16.16 fixed point
-static inline GLfixed mla3a16( GLfixed a0, int32_t b1b0,
-                               GLfixed a1,
-                               GLfixed a2, int32_t b2,
-                               GLint shift,
-                               GLfixed c)
-{
-#if defined(__arm__) && !defined(__thumb__)
-                            
-    GLfixed r;
-    asm(
-        "smulwb %0, %1, %2          \n"
-        "smlawt %0, %3, %2, %0      \n" 
-        "smlawb %0, %4, %5, %0      \n"
-        "add    %0, %7, %0, lsl %6  \n"
-        :   "=&r"(r)
-        :   "r"(a0), "r"(b1b0),
-            "r"(a1),
-            "r"(a2), "r"(b2),
-            "r"(shift),
-            "r"(c)
-        :
-        ); 
-    return r;
-    
-#else
-
-    int32_t accum;
-    int16_t b0 = b1b0 & 0xffff;
-    int16_t b1 = (b1b0 >> 16) & 0xffff;
-    accum  = int64_t(a0)*int16_t(b0) >> 16;
-    accum += int64_t(a1)*int16_t(b1) >> 16;
-    accum += int64_t(a2)*int16_t(b2) >> 16;
-    accum = (accum << shift) + c;
-    return accum;
-
-#endif
-}
-
-
-static inline GLfixed mla3a16_btb( GLfixed a0,
-                                   GLfixed a1,
-                                   GLfixed a2,
-                                   int32_t b1b0, int32_t xxb2,
-                                   GLint shift,
-                                   GLfixed c)
-{
-#if defined(__arm__) && !defined(__thumb__)
-                            
-    GLfixed r;
-    asm(
-        "smulwb %0, %1, %4          \n"
-        "smlawt %0, %2, %4, %0      \n" 
-        "smlawb %0, %3, %5, %0      \n"
-        "add    %0, %7, %0, lsl %6  \n"
-        :   "=&r"(r)
-        :   "r"(a0),
-            "r"(a1),
-            "r"(a2),
-            "r"(b1b0), "r"(xxb2),
-            "r"(shift),
-            "r"(c)
-        :
-        ); 
-    return r;
-    
-#else
-
-    int32_t accum;
-    int16_t b0 =  b1b0        & 0xffff;
-    int16_t b1 = (b1b0 >> 16) & 0xffff;
-    int16_t b2 =  xxb2        & 0xffff;
-    accum  = int64_t(a0)*int16_t(b0) >> 16;
-    accum += int64_t(a1)*int16_t(b1) >> 16;
-    accum += int64_t(a2)*int16_t(b2) >> 16;
-    accum = (accum << shift) + c;
-    return accum;
-
-#endif
-}
-
-static inline GLfixed mla3a16_btt( GLfixed a0,
-                                   GLfixed a1,
-                                   GLfixed a2,
-                                   int32_t b1b0, int32_t b2xx,
-                                   GLint shift,
-                                   GLfixed c)
-{
-#if defined(__arm__) && !defined(__thumb__)
-                            
-    GLfixed r;
-    asm(
-        "smulwb %0, %1, %4          \n"
-        "smlawt %0, %2, %4, %0      \n" 
-        "smlawt %0, %3, %5, %0      \n"
-        "add    %0, %7, %0, lsl %6  \n"
-        :   "=&r"(r)
-        :   "r"(a0),
-            "r"(a1),
-            "r"(a2),
-            "r"(b1b0), "r"(b2xx),
-            "r"(shift),
-            "r"(c)
-        :
-        ); 
-    return r;
-    
-#else
-
-    int32_t accum;
-    int16_t b0 =  b1b0        & 0xffff;
-    int16_t b1 = (b1b0 >> 16) & 0xffff;
-    int16_t b2 = (b2xx >> 16) & 0xffff;
-    accum  = int64_t(a0)*int16_t(b0) >> 16;
-    accum += int64_t(a1)*int16_t(b1) >> 16;
-    accum += int64_t(a2)*int16_t(b2) >> 16;
-    accum = (accum << shift) + c;
-    return accum;
-
-#endif
-}
-
-static inline GLfixed mla3( GLfixed a0, GLfixed b0,
-                            GLfixed a1, GLfixed b1,
-                            GLfixed a2, GLfixed b2)
-{
-#if defined(__arm__) && !defined(__thumb__)
-                            
-    GLfixed r;
-    int32_t t;
-    asm(
-        "smull %0, %1, %2, %3       \n"
-        "smlal %0, %1, %4, %5       \n"
-        "smlal %0, %1, %6, %7       \n"
-        "movs  %0, %0, lsr #16      \n"
-        "adc   %0, %0, %1, lsl #16  \n"
-        :   "=&r"(r), "=&r"(t) 
-        :   "%r"(a0), "r"(b0),
-            "%r"(a1), "r"(b1),
-            "%r"(a2), "r"(b2)
-        :   "cc"
-        ); 
-    return r;
-    
-#else
-
-    return ((   int64_t(a0)*b0 +
-                int64_t(a1)*b1 +
-                int64_t(a2)*b2 + 0x8000)>>16);
-
-#endif
-}
-
-static inline GLfixed mla4( GLfixed a0, GLfixed b0,
-                            GLfixed a1, GLfixed b1,
-                            GLfixed a2, GLfixed b2,
-                            GLfixed a3, GLfixed b3)
-{
-#if defined(__arm__) && !defined(__thumb__)
-                            
-    GLfixed r;
-    int32_t t;
-    asm(
-        "smull %0, %1, %2, %3       \n"
-        "smlal %0, %1, %4, %5       \n"
-        "smlal %0, %1, %6, %7       \n"
-        "smlal %0, %1, %8, %9       \n"
-        "movs  %0, %0, lsr #16      \n"
-        "adc   %0, %0, %1, lsl #16  \n"
-        :   "=&r"(r), "=&r"(t) 
-        :   "%r"(a0), "r"(b0),
-            "%r"(a1), "r"(b1),
-            "%r"(a2), "r"(b2),
-            "%r"(a3), "r"(b3)
-        :   "cc"
-        ); 
-    return r;
-    
-#else
-
-    return ((   int64_t(a0)*b0 +
-                int64_t(a1)*b1 +
-                int64_t(a2)*b2 +
-                int64_t(a3)*b3 + 0x8000)>>16);
-
-#endif
-}
-
-inline
-GLfixed dot4(const GLfixed* a, const GLfixed* b) 
-{
-    return mla4(a[0], b[0], a[1], b[1], a[2], b[2], a[3], b[3]);
-}
-
-
-inline
-GLfixed dot3(const GLfixed* a, const GLfixed* b) 
-{
-    return mla3(a[0], b[0], a[1], b[1], a[2], b[2]);
-}
-
-
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_MATRIX_H
-
diff --git a/opengl/libagl/mipmap.cpp b/opengl/libagl/mipmap.cpp
deleted file mode 100644
index e142a58..0000000
--- a/opengl/libagl/mipmap.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/* libs/opengles/mipmap.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 <stdio.h>
-#include <stdlib.h>
-
-#include "context.h"
-#include "state.h"
-#include "texture.h"
-#include "TextureObjectManager.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex)
-{
-    int level = 0;
-    const GGLSurface* base = &tex->surface;    
-    const GGLFormat& pixelFormat(c->rasterizer.formats[base->format]);
-
-    int w = base->width;
-    int h = base->height;
-    if ((w&h) == 1)
-        return NO_ERROR;
-
-    w = (w>>1) ? : 1;
-    h = (h>>1) ? : 1;
-
-    while(true) {
-        ++level;
-        const int bpr = w * pixelFormat.size;
-        if (tex->reallocate(level, w, h, w,
-                base->format, base->compressedFormat, bpr) != NO_ERROR) {
-            return NO_MEMORY;
-        }
-    
-        int stride = w;
-        int bs = base->stride;
-        GGLSurface& cur = tex->editMip(level);
-
-        if (base->format == GGL_PIXEL_FORMAT_RGB_565)
-        {
-            uint16_t const * src = (uint16_t const *)base->data;
-            uint16_t* dst = (uint16_t*)cur.data;
-            const uint32_t mask = 0x07E0F81F;
-            for (int y=0 ; y<h ; y++) {
-                size_t offset = (y*2) * bs;
-                for (int x=0 ; x<w ; x++) {
-                    uint32_t p00 = src[offset];
-                    uint32_t p10 = src[offset+1];
-                    uint32_t p01 = src[offset+bs];
-                    uint32_t p11 = src[offset+bs+1];
-                    p00 = (p00 | (p00 << 16)) & mask;
-                    p01 = (p01 | (p01 << 16)) & mask;
-                    p10 = (p10 | (p10 << 16)) & mask;
-                    p11 = (p11 | (p11 << 16)) & mask;
-                    uint32_t grb = ((p00 + p10 + p01 + p11) >> 2) & mask;
-                    uint32_t rgb = (grb & 0xFFFF) | (grb >> 16);
-                    dst[x + y*stride] = rgb;
-                    offset += 2;
-                }
-            }
-        }
-        else if (base->format == GGL_PIXEL_FORMAT_RGBA_5551)
-        {
-            uint16_t const * src = (uint16_t const *)base->data;
-            uint16_t* dst = (uint16_t*)cur.data;
-            for (int y=0 ; y<h ; y++) {
-                size_t offset = (y*2) * bs;
-                for (int x=0 ; x<w ; x++) {
-                    uint32_t p00 = src[offset];
-                    uint32_t p10 = src[offset+1];
-                    uint32_t p01 = src[offset+bs];
-                    uint32_t p11 = src[offset+bs+1];
-                    uint32_t r = ((p00>>11)+(p10>>11)+(p01>>11)+(p11>>11)+2)>>2;
-                    uint32_t g = (((p00>>6)+(p10>>6)+(p01>>6)+(p11>>6)+2)>>2)&0x3F;
-                    uint32_t b = ((p00&0x3E)+(p10&0x3E)+(p01&0x3E)+(p11&0x3E)+4)>>3;
-                    uint32_t a = ((p00&1)+(p10&1)+(p01&1)+(p11&1)+2)>>2;
-                    dst[x + y*stride] = (r<<11)|(g<<6)|(b<<1)|a;
-                    offset += 2;
-                }
-            }
-        }
-        else if (base->format == GGL_PIXEL_FORMAT_RGBA_8888)
-        {
-            uint32_t const * src = (uint32_t const *)base->data;
-            uint32_t* dst = (uint32_t*)cur.data;
-            for (int y=0 ; y<h ; y++) {
-                size_t offset = (y*2) * bs;
-                for (int x=0 ; x<w ; x++) {
-                    uint32_t p00 = src[offset];
-                    uint32_t p10 = src[offset+1];
-                    uint32_t p01 = src[offset+bs];
-                    uint32_t p11 = src[offset+bs+1];
-                    uint32_t rb00 = p00 & 0x00FF00FF;
-                    uint32_t rb01 = p01 & 0x00FF00FF;
-                    uint32_t rb10 = p10 & 0x00FF00FF;
-                    uint32_t rb11 = p11 & 0x00FF00FF;
-                    uint32_t ga00 = (p00 >> 8) & 0x00FF00FF;
-                    uint32_t ga01 = (p01 >> 8) & 0x00FF00FF;
-                    uint32_t ga10 = (p10 >> 8) & 0x00FF00FF;
-                    uint32_t ga11 = (p11 >> 8) & 0x00FF00FF;
-                    uint32_t rb = (rb00 + rb01 + rb10 + rb11)>>2;
-                    uint32_t ga = (ga00 + ga01 + ga10 + ga11)>>2;
-                    uint32_t rgba = (rb & 0x00FF00FF) | ((ga & 0x00FF00FF)<<8);
-                    dst[x + y*stride] = rgba;
-                    offset += 2;
-                }
-            }
-        }
-        else if ((base->format == GGL_PIXEL_FORMAT_RGB_888) ||
-                 (base->format == GGL_PIXEL_FORMAT_LA_88) ||
-                 (base->format == GGL_PIXEL_FORMAT_A_8) ||
-                 (base->format == GGL_PIXEL_FORMAT_L_8))
-        {
-            int skip;
-            switch (base->format) {
-            case GGL_PIXEL_FORMAT_RGB_888:  skip = 3;   break;
-            case GGL_PIXEL_FORMAT_LA_88:    skip = 2;   break;
-            default:                        skip = 1;   break;
-            }
-            uint8_t const * src = (uint8_t const *)base->data;
-            uint8_t* dst = (uint8_t*)cur.data;            
-            bs *= skip;
-            stride *= skip;
-            for (int y=0 ; y<h ; y++) {
-                size_t offset = (y*2) * bs;
-                for (int x=0 ; x<w ; x++) {
-                    for (int c=0 ; c<skip ; c++) {
-                        uint32_t p00 = src[c+offset];
-                        uint32_t p10 = src[c+offset+skip];
-                        uint32_t p01 = src[c+offset+bs];
-                        uint32_t p11 = src[c+offset+bs+skip];
-                        dst[x + y*stride + c] = (p00 + p10 + p01 + p11) >> 2;
-                    }
-                    offset += 2*skip;
-                }
-            }
-        }
-        else if (base->format == GGL_PIXEL_FORMAT_RGBA_4444)
-        {
-            uint16_t const * src = (uint16_t const *)base->data;
-            uint16_t* dst = (uint16_t*)cur.data;
-            for (int y=0 ; y<h ; y++) {
-                size_t offset = (y*2) * bs;
-                for (int x=0 ; x<w ; x++) {
-                    uint32_t p00 = src[offset];
-                    uint32_t p10 = src[offset+1];
-                    uint32_t p01 = src[offset+bs];
-                    uint32_t p11 = src[offset+bs+1];
-                    p00 = ((p00 << 12) & 0x0F0F0000) | (p00 & 0x0F0F);
-                    p10 = ((p10 << 12) & 0x0F0F0000) | (p10 & 0x0F0F);
-                    p01 = ((p01 << 12) & 0x0F0F0000) | (p01 & 0x0F0F);
-                    p11 = ((p11 << 12) & 0x0F0F0000) | (p11 & 0x0F0F);
-                    uint32_t rbga = (p00 + p10 + p01 + p11) >> 2;
-                    uint32_t rgba = (rbga & 0x0F0F) | ((rbga>>12) & 0xF0F0);
-                    dst[x + y*stride] = rgba;
-                    offset += 2;
-                }
-            }
-        } else {
-            ALOGE("Unsupported format (%d)", base->format);
-            return BAD_TYPE;
-        }
-
-        // exit condition: we just processed the 1x1 LODs
-        if ((w&h) == 1)
-            break;
-
-        base = &cur;
-        w = (w>>1) ? : 1;
-        h = (h>>1) ? : 1;
-    }
-    return NO_ERROR;
-}
-
-}; // namespace android
diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp
deleted file mode 100644
index d3b19e8..0000000
--- a/opengl/libagl/primitives.cpp
+++ /dev/null
@@ -1,1112 +0,0 @@
-/* libs/opengles/primitives.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-
-#include "context.h"
-#include "primitives.h"
-#include "light.h"
-#include "matrix.h"
-#include "vertex.h"
-#include "fp.h"
-#include "TextureObjectManager.h"
-
-extern "C" void iterators0032(const void* that,
-        int32_t* it, int32_t c0, int32_t c1, int32_t c2);
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static void primitive_point(ogles_context_t* c, vertex_t* v);
-static void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1);
-static void primitive_clip_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static void primitive_nop_point(ogles_context_t* c, vertex_t* v);
-static void primitive_nop_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1);
-static void primitive_nop_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static inline bool cull_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static void lerp_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static void lerp_texcoords(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static void lerp_texcoords_w(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static void triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static void clip_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2);
-
-static unsigned int clip_line(ogles_context_t* c,
-        vertex_t* s, vertex_t* p);
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-static void lightTriangleDarkSmooth(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    if (!(v0->flags & vertex_t::LIT)) {
-        v0->flags |= vertex_t::LIT;
-        const GLvoid* cp = c->arrays.color.element(
-                v0->index & vertex_cache_t::INDEX_MASK);
-        c->arrays.color.fetch(c, v0->color.v, cp);
-    }
-    if (!(v1->flags & vertex_t::LIT)) {
-        v1->flags |= vertex_t::LIT;
-        const GLvoid* cp = c->arrays.color.element(
-                v1->index & vertex_cache_t::INDEX_MASK);
-        c->arrays.color.fetch(c, v1->color.v, cp);
-    }
-    if(!(v2->flags & vertex_t::LIT)) {
-        v2->flags |= vertex_t::LIT;
-        const GLvoid* cp = c->arrays.color.element(
-                v2->index & vertex_cache_t::INDEX_MASK);
-        c->arrays.color.fetch(c, v2->color.v, cp);
-    }
-}
-
-static void lightTriangleDarkFlat(ogles_context_t* c,
-        vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* v2)
-{
-    if (!(v2->flags & vertex_t::LIT)) {
-        v2->flags |= vertex_t::LIT;
-        const GLvoid* cp = c->arrays.color.element(
-                v2->index & vertex_cache_t::INDEX_MASK);
-        c->arrays.color.fetch(c, v2->color.v, cp);
-    }
-    // configure the rasterizer here, before we clip
-    c->rasterizer.procs.color4xv(c, v2->color.v);
-}
-
-static void lightTriangleSmooth(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    if (!(v0->flags & vertex_t::LIT))
-        c->lighting.lightVertex(c, v0);
-    if (!(v1->flags & vertex_t::LIT))
-        c->lighting.lightVertex(c, v1);
-    if(!(v2->flags & vertex_t::LIT))
-        c->lighting.lightVertex(c, v2);
-}
-
-static void lightTriangleFlat(ogles_context_t* c,
-        vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* v2)
-{
-    if (!(v2->flags & vertex_t::LIT))
-        c->lighting.lightVertex(c, v2);
-    // configure the rasterizer here, before we clip
-    c->rasterizer.procs.color4xv(c, v2->color.v);
-}
-
-// The fog versions...
-
-static inline
-void lightVertexDarkSmoothFog(ogles_context_t* c, vertex_t* v)
-{
-    if (!(v->flags & vertex_t::LIT)) {
-        v->flags |= vertex_t::LIT;
-        v->fog = c->fog.fog(c, v->eye.z);
-        const GLvoid* cp = c->arrays.color.element(
-                v->index & vertex_cache_t::INDEX_MASK);
-        c->arrays.color.fetch(c, v->color.v, cp);
-    }
-}
-static inline
-void lightVertexDarkFlatFog(ogles_context_t* c, vertex_t* v)
-{
-    if (!(v->flags & vertex_t::LIT)) {
-        v->flags |= vertex_t::LIT;
-        v->fog = c->fog.fog(c, v->eye.z);
-    }
-}
-static inline
-void lightVertexSmoothFog(ogles_context_t* c, vertex_t* v)
-{
-    if (!(v->flags & vertex_t::LIT)) {
-        v->fog = c->fog.fog(c, v->eye.z);
-        c->lighting.lightVertex(c, v);
-    }
-}
-
-static void lightTriangleDarkSmoothFog(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    lightVertexDarkSmoothFog(c, v0);
-    lightVertexDarkSmoothFog(c, v1);
-    lightVertexDarkSmoothFog(c, v2);
-}
-
-static void lightTriangleDarkFlatFog(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    lightVertexDarkFlatFog(c, v0);
-    lightVertexDarkFlatFog(c, v1);
-    lightVertexDarkSmoothFog(c, v2);
-    // configure the rasterizer here, before we clip
-    c->rasterizer.procs.color4xv(c, v2->color.v);
-}
-
-static void lightTriangleSmoothFog(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    lightVertexSmoothFog(c, v0);
-    lightVertexSmoothFog(c, v1);
-    lightVertexSmoothFog(c, v2);
-}
-
-static void lightTriangleFlatFog(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    lightVertexDarkFlatFog(c, v0);
-    lightVertexDarkFlatFog(c, v1);
-    lightVertexSmoothFog(c, v2);
-    // configure the rasterizer here, before we clip
-    c->rasterizer.procs.color4xv(c, v2->color.v);
-}
-
-
-
-typedef void (*light_primitive_t)(ogles_context_t*,
-        vertex_t*, vertex_t*, vertex_t*);
-
-// fog 0x4, light 0x2, smooth 0x1
-static const light_primitive_t lightPrimitive[8] = {
-    lightTriangleDarkFlat,          // no fog | dark  | flat
-    lightTriangleDarkSmooth,        // no fog | dark  | smooth
-    lightTriangleFlat,              // no fog | light | flat
-    lightTriangleSmooth,            // no fog | light | smooth
-    lightTriangleDarkFlatFog,       // fog    | dark  | flat
-    lightTriangleDarkSmoothFog,     // fog    | dark  | smooth
-    lightTriangleFlatFog,           // fog    | light | flat
-    lightTriangleSmoothFog          // fog    | light | smooth
-};
-
-void ogles_validate_primitives(ogles_context_t* c)
-{
-    const uint32_t enables = c->rasterizer.state.enables;
-
-    // set up the lighting/shading/smoothing/fogging function
-    int index = enables & GGL_ENABLE_SMOOTH ? 0x1 : 0;
-    index |= c->lighting.enable ? 0x2 : 0;
-    index |= enables & GGL_ENABLE_FOG ? 0x4 : 0;
-    c->lighting.lightTriangle = lightPrimitive[index];
-    
-    // set up the primitive renderers
-    if (ggl_likely(c->arrays.vertex.enable)) {
-        c->prims.renderPoint    = primitive_point;
-        c->prims.renderLine     = primitive_line;
-        c->prims.renderTriangle = primitive_clip_triangle;
-    } else {
-        c->prims.renderPoint    = primitive_nop_point;
-        c->prims.renderLine     = primitive_nop_line;
-        c->prims.renderTriangle = primitive_nop_triangle;
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-void compute_iterators_t::initTriangle(
-        vertex_t const* v0, vertex_t const* v1, vertex_t const* v2)
-{
-    m_dx01 = v1->window.x - v0->window.x;
-    m_dy10 = v0->window.y - v1->window.y;
-    m_dx20 = v0->window.x - v2->window.x;
-    m_dy02 = v2->window.y - v0->window.y;
-    m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20;
-    (void)m_reserved; // suppress unused warning
-}
-
-void compute_iterators_t::initLine(
-        vertex_t const* v0, vertex_t const* v1)
-{
-    m_dx01 = m_dy02 = v1->window.x - v0->window.x;
-    m_dy10 = m_dx20 = v0->window.y - v1->window.y;
-    m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20;
-}
-
-void compute_iterators_t::initLerp(vertex_t const* v0, uint32_t enables)
-{
-    m_x0 = v0->window.x;
-    m_y0 = v0->window.y;
-    const GGLcoord area = (m_area + TRI_HALF) >> TRI_FRACTION_BITS;
-    const GGLcoord minArea = 2; // cannot be inverted
-    // triangles with an area smaller than 1.0 are not smooth-shaded
-
-    int q=0, s=0, d=0;
-    if (abs(area) >= minArea) {
-        // Here we do some voodoo magic, to compute a suitable scale
-        // factor for deltas/area:
-
-        // First compute the 1/area with full 32-bits precision,
-        // gglRecipQNormalized returns a number [-0.5, 0.5[ and an exponent.
-        d = gglRecipQNormalized(area, &q);
-
-        // Then compute the minimum left-shift to not overflow the muls
-        // below. 
-        s = 32 - gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20));
-
-        // We'll keep 16-bits of precision for deltas/area. So we need
-        // to shift everything left an extra 15 bits.
-        s += 15;
-        
-        // make sure all final shifts are not > 32, because gglMulx
-        // can't handle it.
-        if (s < q) s = q;
-        if (s > 32) {
-            d >>= 32-s;
-            s = 32;
-        }
-    }
-
-    m_dx01 = gglMulx(m_dx01, d, s);
-    m_dy10 = gglMulx(m_dy10, d, s);
-    m_dx20 = gglMulx(m_dx20, d, s);
-    m_dy02 = gglMulx(m_dy02, d, s);
-    m_area_scale = 32 + q - s;
-    m_scale = 0;
-
-    if (enables & GGL_ENABLE_TMUS) {
-        const int A = gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20));
-        const int B = gglClz(abs(m_x0)|abs(m_y0));
-        m_scale = max(0, 32 - (A + 16)) +
-                  max(0, 32 - (B + TRI_FRACTION_BITS)) + 1;
-    }
-}
-
-int compute_iterators_t::iteratorsScale(GGLfixed* it,
-        int32_t c0, int32_t c1, int32_t c2) const
-{
-    int32_t dc01 = c1 - c0;
-    int32_t dc02 = c2 - c0;
-    const int A = gglClz(abs(c0));
-    const int B = gglClz(abs(dc01)|abs(dc02));
-    const int scale = min(A, B - m_scale) - 2;
-    if (scale >= 0) {
-        c0   <<= scale;
-        dc01 <<= scale;
-        dc02 <<= scale;
-    } else {
-        c0   >>= -scale;
-        dc01 >>= -scale;
-        dc02 >>= -scale;
-    }
-    const int s = m_area_scale;
-    int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s);
-    int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s);
-    int32_t c = c0 - (gglMulAddx(dcdx, m_x0, 
-            gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS));
-    it[0] = c;
-    it[1] = dcdx;
-    it[2] = dcdy;
-    return scale;
-}
-
-void compute_iterators_t::iterators1616(GGLfixed* it,
-        GGLfixed c0, GGLfixed c1, GGLfixed c2) const
-{
-    const GGLfixed dc01 = c1 - c0;
-    const GGLfixed dc02 = c2 - c0;
-    // 16.16 x 16.16 == 32.32 --> 16.16
-    const int s = m_area_scale;
-    int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s);
-    int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s);
-    int32_t c = c0 - (gglMulAddx(dcdx, m_x0,
-            gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS));
-    it[0] = c;
-    it[1] = dcdx;
-    it[2] = dcdy;
-}
-
-void compute_iterators_t::iterators0032(int64_t* it,
-        int32_t c0, int32_t c1, int32_t c2) const
-{
-    const int s = m_area_scale - 16;
-    int32_t dc01 = (c1 - c0)>>s;
-    int32_t dc02 = (c2 - c0)>>s;
-    // 16.16 x 16.16 == 32.32
-    int64_t dcdx = gglMulii(dc01, m_dy02) + gglMulii(dc02, m_dy10);
-    int64_t dcdy = gglMulii(dc02, m_dx01) + gglMulii(dc01, m_dx20);
-    it[ 0] = (c0<<16) - ((dcdx*m_x0 + dcdy*m_y0)>>4);
-    it[ 1] = dcdx;
-    it[ 2] = dcdy;
-}
-
-#if defined(__arm__) && !defined(__thumb__)
-inline void compute_iterators_t::iterators0032(int32_t* it,
-        int32_t c0, int32_t c1, int32_t c2) const
-{
-    ::iterators0032(this, it, c0, c1, c2);
-}
-#else
-void compute_iterators_t::iterators0032(int32_t* it,
-        int32_t c0, int32_t c1, int32_t c2) const
-{
-    int64_t it64[3];
-    iterators0032(it64, c0, c1, c2);
-    it[0] = it64[0];
-    it[1] = it64[1];
-    it[2] = it64[2];
-}
-#endif
-
-// ----------------------------------------------------------------------------
-
-static inline int32_t clampZ(GLfixed z) CONST;
-int32_t clampZ(GLfixed z) {
-    z = (z & ~(z>>31));
-    if (z >= 0x10000)
-        z = 0xFFFF;
-    return z;
-}
-
-static __attribute__((noinline))
-void fetch_texcoord_impl(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    vertex_t* const vtx[3] = { v0, v1, v2 };
-    array_t const * const texcoordArray = c->arrays.texture;
-    
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        if (!(c->rasterizer.state.texture[i].enable))
-            continue;
-        
-        for (int j=0 ; j<3 ; j++) {
-            vertex_t* const v = vtx[j];
-            if (v->flags & vertex_t::TT)
-                continue;
-
-            // NOTE: here we could compute automatic texgen
-            // such as sphere/cube maps, instead of fetching them
-            // from the textcoord array.
-
-            vec4_t& coords = v->texture[i];
-            const GLubyte* tp = texcoordArray[i].element(
-                    v->index & vertex_cache_t::INDEX_MASK);
-            texcoordArray[i].fetch(c, coords.v, tp);
-
-            // transform texture coordinates...
-            coords.Q = 0x10000;
-            const transform_t& tr = c->transforms.texture[i].transform; 
-            if (ggl_unlikely(tr.ops)) {
-                c->arrays.tex_transform[i](&tr, &coords, &coords);
-            }
-
-            // divide by Q
-            const GGLfixed q = coords.Q;
-            if (ggl_unlikely(q != 0x10000)) {
-                const int32_t qinv = gglRecip28(q);
-                coords.S = gglMulx(coords.S, qinv, 28);
-                coords.T = gglMulx(coords.T, qinv, 28);
-            }
-        }
-    }
-    v0->flags |= vertex_t::TT;
-    v1->flags |= vertex_t::TT;
-    v2->flags |= vertex_t::TT;
-}
-
-inline void fetch_texcoord(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    const uint32_t enables = c->rasterizer.state.enables;
-    if (!(enables & GGL_ENABLE_TMUS))
-        return;
-
-    // Fetch & transform texture coordinates...
-    if (ggl_likely(v0->flags & v1->flags & v2->flags & vertex_t::TT)) {
-        // already done for all three vertices, bail...
-        return;
-    }
-    fetch_texcoord_impl(c, v0, v1, v2);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Point
-#endif
-
-void primitive_nop_point(ogles_context_t*, vertex_t*) {
-}
-
-void primitive_point(ogles_context_t* c, vertex_t* v)
-{
-    // lighting & clamping...
-    const uint32_t enables = c->rasterizer.state.enables;
-
-    if (ggl_unlikely(!(v->flags & vertex_t::LIT))) {
-        if (c->lighting.enable) {
-            c->lighting.lightVertex(c, v);
-        } else {
-            v->flags |= vertex_t::LIT;
-            const GLvoid* cp = c->arrays.color.element(
-                    v->index & vertex_cache_t::INDEX_MASK);
-            c->arrays.color.fetch(c, v->color.v, cp);
-        }
-        if (enables & GGL_ENABLE_FOG) {
-            v->fog = c->fog.fog(c, v->eye.z);
-        }
-    }
-
-    // XXX: we don't need to do that each-time
-    // if color array and lighting not enabled 
-    c->rasterizer.procs.color4xv(c, v->color.v);
-
-    // XXX: look into ES point-sprite extension
-    if (enables & GGL_ENABLE_TMUS) {
-        fetch_texcoord(c, v,v,v);
-        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-            if (!c->rasterizer.state.texture[i].enable) 
-                continue;
-            int32_t itt[8];
-            itt[1] = itt[2] = itt[4] = itt[5] = 0;
-            itt[6] = itt[7] = 16; // XXX: check that
-            if (c->rasterizer.state.texture[i].s_wrap == GGL_CLAMP) {
-                int width = c->textures.tmu[i].texture->surface.width;
-                itt[0] = v->texture[i].S * width;
-                itt[6] = 0;
-            }
-            if (c->rasterizer.state.texture[i].t_wrap == GGL_CLAMP) {
-                int height = c->textures.tmu[i].texture->surface.height;
-                itt[3] = v->texture[i].T * height;
-                itt[7] = 0;
-            }
-            c->rasterizer.procs.texCoordGradScale8xv(c, i, itt);
-        }
-    }
-    
-    if (enables & GGL_ENABLE_DEPTH_TEST) {
-        int32_t itz[3];
-        itz[0] = clampZ(v->window.z) * 0x00010001;
-        itz[1] = itz[2] = 0;
-        c->rasterizer.procs.zGrad3xv(c, itz);
-    }
-
-    if (enables & GGL_ENABLE_FOG) {
-        GLfixed itf[3];
-        itf[0] = v->fog;
-        itf[1] = itf[2] = 0;
-        c->rasterizer.procs.fogGrad3xv(c, itf);
-    }
-
-    // Render our point...
-    c->rasterizer.procs.pointx(c, v->window.v, c->point.size);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Line
-#endif
-
-void primitive_nop_line(ogles_context_t*, vertex_t*, vertex_t*) {
-}
-
-void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1)
-{
-    // get texture coordinates
-    fetch_texcoord(c, v0, v1, v1);
-
-    // light/shade the vertices first (they're copied below)
-    c->lighting.lightTriangle(c, v0, v1, v1);
-
-    // clip the line if needed
-    if (ggl_unlikely((v0->flags | v1->flags) & vertex_t::CLIP_ALL)) {
-        unsigned int count = clip_line(c, v0, v1);
-        if (ggl_unlikely(count == 0))
-            return;
-    }
-
-    // compute iterators...
-    const uint32_t enables = c->rasterizer.state.enables;
-    const uint32_t mask =   GGL_ENABLE_TMUS |
-                            GGL_ENABLE_SMOOTH |
-                            GGL_ENABLE_W | 
-                            GGL_ENABLE_FOG |
-                            GGL_ENABLE_DEPTH_TEST;
-
-    if (ggl_unlikely(enables & mask)) {
-        c->lerp.initLine(v0, v1);
-        lerp_triangle(c, v0, v1, v0);
-    }
-
-    // render our line
-    c->rasterizer.procs.linex(c, v0->window.v, v1->window.v, c->line.width);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Triangle
-#endif
-
-void primitive_nop_triangle(ogles_context_t* /*c*/,
-        vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* /*v2*/) {
-}
-
-void primitive_clip_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    uint32_t cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL;
-    if (ggl_likely(!cc)) {
-        // code below must be as optimized as possible, this is the
-        // common code path.
-
-        // This triangle is not clipped, test if it's culled
-        // unclipped triangle...
-        c->lerp.initTriangle(v0, v1, v2);
-        if (cull_triangle(c, v0, v1, v2))
-            return; // culled!
-
-        // Fetch all texture coordinates if needed
-        fetch_texcoord(c, v0, v1, v2);
-
-        // light (or shade) our triangle!
-        c->lighting.lightTriangle(c, v0, v1, v2);
-
-        triangle(c, v0, v1, v2);
-        return;
-    }
-
-    // The assumption here is that we're not going to clip very often,
-    // and even more rarely will we clip a triangle that ends up
-    // being culled out. So it's okay to light the vertices here, even though
-    // in a few cases we won't render the triangle (if culled).
-
-    // Fetch texture coordinates...
-    fetch_texcoord(c, v0, v1, v2);
-
-    // light (or shade) our triangle!
-    c->lighting.lightTriangle(c, v0, v1, v2);
-
-    clip_triangle(c, v0, v1, v2);
-}
-
-// -----------------------------------------------------------------------
-
-void triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    // compute iterators...
-    const uint32_t enables = c->rasterizer.state.enables;
-    const uint32_t mask =   GGL_ENABLE_TMUS |
-                            GGL_ENABLE_SMOOTH |
-                            GGL_ENABLE_W | 
-                            GGL_ENABLE_FOG |
-                            GGL_ENABLE_DEPTH_TEST;
-
-    if (ggl_likely(enables & mask))
-        lerp_triangle(c, v0, v1, v2);
-
-    c->rasterizer.procs.trianglex(c, v0->window.v, v1->window.v, v2->window.v);
-}
-
-void lerp_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    const uint32_t enables = c->rasterizer.state.enables;
-    c->lerp.initLerp(v0, enables);
-
-    // set up texture iterators
-    if (enables & GGL_ENABLE_TMUS) {
-        if (enables & GGL_ENABLE_W) {
-            lerp_texcoords_w(c, v0, v1, v2);
-        } else {
-            lerp_texcoords(c, v0, v1, v2);
-        }
-    }
-
-    // set up the color iterators
-    const compute_iterators_t& lerp = c->lerp;
-    if (enables & GGL_ENABLE_SMOOTH) {
-        GLfixed itc[12];
-        for (int i=0 ; i<4 ; i++) {
-            const GGLcolor c0 = v0->color.v[i] * 255;
-            const GGLcolor c1 = v1->color.v[i] * 255;
-            const GGLcolor c2 = v2->color.v[i] * 255;
-            lerp.iterators1616(&itc[i*3], c0, c1, c2);
-        }
-        c->rasterizer.procs.colorGrad12xv(c, itc);
-    }
-
-    if (enables & GGL_ENABLE_DEPTH_TEST) {
-        int32_t itz[3];
-        const int32_t v0z = clampZ(v0->window.z);
-        const int32_t v1z = clampZ(v1->window.z);
-        const int32_t v2z = clampZ(v2->window.z);
-        if (ggl_unlikely(c->polygonOffset.enable)) {
-            const int32_t units = (c->polygonOffset.units << 16);
-            const GLfixed factor = c->polygonOffset.factor;
-            if (factor) {
-                int64_t itz64[3];
-                lerp.iterators0032(itz64, v0z, v1z, v2z);
-                int64_t maxDepthSlope = max(itz64[1], itz64[2]);
-                itz[0] = uint32_t(itz64[0]) 
-                        + uint32_t((maxDepthSlope*factor)>>16) + units;
-                itz[1] = uint32_t(itz64[1]);
-                itz[2] = uint32_t(itz64[2]);
-            } else {
-                lerp.iterators0032(itz, v0z, v1z, v2z);
-                itz[0] += units; 
-            }
-        } else {
-            lerp.iterators0032(itz, v0z, v1z, v2z);
-        }
-        c->rasterizer.procs.zGrad3xv(c, itz);
-    }    
-
-    if (ggl_unlikely(enables & GGL_ENABLE_FOG)) {
-        GLfixed itf[3];
-        lerp.iterators1616(itf, v0->fog, v1->fog, v2->fog);
-        c->rasterizer.procs.fogGrad3xv(c, itf);
-    }
-}
-
-
-static inline
-int compute_lod(ogles_context_t* c, int i,
-        int32_t s0, int32_t t0, int32_t s1, int32_t t1, int32_t s2, int32_t t2)
-{
-    // Compute mipmap level / primitive
-    // rho = sqrt( texelArea / area )
-    // lod = log2( rho )
-    // lod = log2( texelArea / area ) / 2
-    // lod = (log2( texelArea ) - log2( area )) / 2
-    const compute_iterators_t& lerp = c->lerp;
-    const GGLcoord area = abs(lerp.area());
-    const int w = c->textures.tmu[i].texture->surface.width;
-    const int h = c->textures.tmu[i].texture->surface.height;
-    const int shift = 16 + (16 - TRI_FRACTION_BITS);
-    int32_t texelArea = abs( gglMulx(s1-s0, t2-t0, shift) -
-            gglMulx(s2-s0, t1-t0, shift) )*w*h;
-    int log2TArea = (32-TRI_FRACTION_BITS  -1) - gglClz(texelArea);
-    int log2Area  = (32-TRI_FRACTION_BITS*2-1) - gglClz(area);
-    int lod = (log2TArea - log2Area + 1) >> 1;
-    return lod;
-}
-
-void lerp_texcoords(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    const compute_iterators_t& lerp = c->lerp;
-    int32_t itt[8] __attribute__((aligned(16)));
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        const texture_t& tmu = c->rasterizer.state.texture[i];
-        if (!tmu.enable) 
-            continue;
-
-        // compute the jacobians using block floating-point
-        int32_t s0 = v0->texture[i].S;
-        int32_t t0 = v0->texture[i].T;
-        int32_t s1 = v1->texture[i].S;
-        int32_t t1 = v1->texture[i].T;
-        int32_t s2 = v2->texture[i].S;
-        int32_t t2 = v2->texture[i].T;
-
-        const GLenum min_filter = c->textures.tmu[i].texture->min_filter;
-        if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) {
-            int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2);
-            c->rasterizer.procs.bindTextureLod(c, i,
-                    &c->textures.tmu[i].texture->mip(lod));
-        }
-
-        // premultiply (s,t) when clampling
-        if (tmu.s_wrap == GGL_CLAMP) {
-            const int width = tmu.surface.width;
-            s0 *= width;
-            s1 *= width;
-            s2 *= width;
-        }
-        if (tmu.t_wrap == GGL_CLAMP) {
-            const int height = tmu.surface.height;
-            t0 *= height;
-            t1 *= height;
-            t2 *= height;
-        }
-        itt[6] = -lerp.iteratorsScale(itt+0, s0, s1, s2);
-        itt[7] = -lerp.iteratorsScale(itt+3, t0, t1, t2);
-        c->rasterizer.procs.texCoordGradScale8xv(c, i, itt);
-    }
-}
-
-void lerp_texcoords_w(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    const compute_iterators_t& lerp = c->lerp;
-    int32_t itt[8] __attribute__((aligned(16)));
-    int32_t itw[3];
-
-    // compute W's scale to 2.30
-    int32_t w0 = v0->window.w;
-    int32_t w1 = v1->window.w;
-    int32_t w2 = v2->window.w;
-    int wscale = 32 - gglClz(w0|w1|w2);
-
-    // compute the jacobian using block floating-point    
-    int sc = lerp.iteratorsScale(itw, w0, w1, w2);
-    sc +=  wscale - 16;
-    c->rasterizer.procs.wGrad3xv(c, itw);
-
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        const texture_t& tmu = c->rasterizer.state.texture[i];
-        if (!tmu.enable) 
-            continue;
-
-        // compute the jacobians using block floating-point
-        int32_t s0 = v0->texture[i].S;
-        int32_t t0 = v0->texture[i].T;
-        int32_t s1 = v1->texture[i].S;
-        int32_t t1 = v1->texture[i].T;
-        int32_t s2 = v2->texture[i].S;
-        int32_t t2 = v2->texture[i].T;
-
-        const GLenum min_filter = c->textures.tmu[i].texture->min_filter;
-        if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) {
-            int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2);
-            c->rasterizer.procs.bindTextureLod(c, i,
-                    &c->textures.tmu[i].texture->mip(lod));
-        }
-
-        // premultiply (s,t) when clampling
-        if (tmu.s_wrap == GGL_CLAMP) {
-            const int width = tmu.surface.width;
-            s0 *= width;
-            s1 *= width;
-            s2 *= width;
-        }
-        if (tmu.t_wrap == GGL_CLAMP) {
-            const int height = tmu.surface.height;
-            t0 *= height;
-            t1 *= height;
-            t2 *= height;
-        }
-
-        s0 = gglMulx(s0, w0, wscale);
-        t0 = gglMulx(t0, w0, wscale);
-        s1 = gglMulx(s1, w1, wscale);
-        t1 = gglMulx(t1, w1, wscale);
-        s2 = gglMulx(s2, w2, wscale);
-        t2 = gglMulx(t2, w2, wscale);
-
-        itt[6] = sc - lerp.iteratorsScale(itt+0, s0, s1, s2);
-        itt[7] = sc - lerp.iteratorsScale(itt+3, t0, t1, t2);
-        c->rasterizer.procs.texCoordGradScale8xv(c, i, itt);
-    }
-}
-
-
-static inline
-bool cull_triangle(ogles_context_t* c, vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* /*v2*/)
-{
-    if (ggl_likely(c->cull.enable)) {
-        const GLenum winding = (c->lerp.area() > 0) ? GL_CW : GL_CCW;
-        const GLenum face = (winding == c->cull.frontFace) ? GL_FRONT : GL_BACK;
-        if (face == c->cull.cullFace)
-            return true; // culled!
-    }
-    return false;
-}
-
-static inline
-GLfixed frustumPlaneDist(int plane, const vec4_t& s)
-{
-    const GLfixed d = s.v[ plane >> 1 ];
-    return  ((plane & 1) ? (s.w - d) : (s.w + d)); 
-}
-
-static inline
-int32_t clipDivide(GLfixed a, GLfixed b) {
-    // returns a 4.28 fixed-point
-    return gglMulDivi(1LU<<28, a, b);
-} 
-
-void clip_triangle(ogles_context_t* c,
-        vertex_t* v0, vertex_t* v1, vertex_t* v2)
-{
-    uint32_t all_cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL;
-
-    vertex_t *p0, *p1, *p2;
-    const int MAX_CLIPPING_PLANES = 6 + OGLES_MAX_CLIP_PLANES;
-    const int MAX_VERTICES = 3;
-
-    // Temporary buffer to hold the new vertices. Each plane can add up to 
-    // two new vertices (because the polygon is convex).
-    // We need one extra element, to handle an overflow case when
-    // the polygon degenerates into something non convex.
-    vertex_t buffer[MAX_CLIPPING_PLANES * 2 + 1];   // ~3KB
-    vertex_t* buf = buffer;
-
-    // original list of vertices (polygon to clip, in fact this
-    // function works with an arbitrary polygon).
-    vertex_t* in[3] = { v0, v1, v2 };
-    
-    // output lists (we need 2, which we use back and forth)
-    // (maximum outpout list's size is MAX_CLIPPING_PLANES + MAX_VERTICES)
-    // 2 more elements for overflow when non convex polygons.
-    vertex_t* out[2][MAX_CLIPPING_PLANES + MAX_VERTICES + 2];
-    unsigned int outi = 0;
-    
-    // current input list
-    vertex_t** ivl = in;
-
-    // 3 input vertices, 0 in the output list, first plane
-    unsigned int ic = 3;
-
-    // User clip-planes first, the clipping is always done in eye-coordinate
-    // this is basically the same algorithm than for the view-volume
-    // clipping, except for the computation of the distance (vertex, plane)
-    // and the fact that we need to compute the eye-coordinates of each
-    // new vertex we create.
-    
-    if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL))
-    {
-        unsigned int plane = 0;
-        uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8;
-        do {
-            if (cc & 1) {        
-                // pointers to our output list (head and current)
-                vertex_t** const ovl = &out[outi][0];
-                vertex_t** output = ovl;
-                unsigned int oc = 0;
-                unsigned int sentinel = 0;
-                // previous vertex, compute distance to the plane
-                vertex_t* s = ivl[ic-1];
-                const vec4_t& equation = c->clipPlanes.plane[plane].equation;
-                GLfixed sd = dot4(equation.v, s->eye.v);
-                // clip each vertex against this plane...
-                for (unsigned int i=0 ; i<ic ; i++) {            
-                    vertex_t* p = ivl[i];
-                    const GLfixed pd = dot4(equation.v, p->eye.v);
-                    if (sd >= 0) {
-                        if (pd >= 0) {
-                            // both inside
-                            *output++ = p;
-                            oc++;
-                        } else {
-                            // s inside, p outside (exiting)
-                            const GLfixed t = clipDivide(sd, sd-pd);
-                            c->arrays.clipEye(c, buf, t, p, s);
-                            *output++ = buf++;
-                            oc++;
-                            if (++sentinel >= 3)
-                                return; // non-convex polygon!
-                        }
-                    } else {
-                        if (pd >= 0) {
-                            // s outside (entering)
-                            if (pd) {
-                                const GLfixed t = clipDivide(pd, pd-sd);
-                                c->arrays.clipEye(c, buf, t, s, p);
-                                *output++ = buf++;
-                                oc++;
-                                if (++sentinel >= 3)
-                                    return; // non-convex polygon!
-                            }
-                            *output++ = p;
-                            oc++;
-                        } else {
-                           // both outside
-                        }
-                    }
-                    s = p;
-                    sd = pd;
-                }
-                // output list become the new input list
-                if (oc<3)
-                    return; // less than 3 vertices left? we're done!
-                ivl = ovl;
-                ic = oc;
-                outi = 1-outi;
-            }
-            cc >>= 1;
-            plane++;
-        } while (cc);
-    }
-
-    // frustum clip-planes
-    if (all_cc & vertex_t::FRUSTUM_CLIP_ALL)
-    {
-        unsigned int plane = 0;
-        uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL;
-        do {
-            if (cc & 1) {        
-                // pointers to our output list (head and current)
-                vertex_t** const ovl = &out[outi][0];
-                vertex_t** output = ovl;
-                unsigned int oc = 0;
-                unsigned int sentinel = 0;
-                // previous vertex, compute distance to the plane
-                vertex_t* s = ivl[ic-1];
-                GLfixed sd = frustumPlaneDist(plane, s->clip);
-                // clip each vertex against this plane...
-                for (unsigned int i=0 ; i<ic ; i++) {            
-                    vertex_t* p = ivl[i];
-                    const GLfixed pd = frustumPlaneDist(plane, p->clip);
-                    if (sd >= 0) {
-                        if (pd >= 0) {
-                            // both inside
-                            *output++ = p;
-                            oc++;
-                        } else {
-                            // s inside, p outside (exiting)
-                            const GLfixed t = clipDivide(sd, sd-pd);
-                            c->arrays.clipVertex(c, buf, t, p, s);
-                            *output++ = buf++;
-                            oc++;
-                            if (++sentinel >= 3)
-                                return; // non-convex polygon!
-                        }
-                    } else {
-                        if (pd >= 0) {
-                            // s outside (entering)
-                            if (pd) {
-                                const GLfixed t = clipDivide(pd, pd-sd);
-                                c->arrays.clipVertex(c, buf, t, s, p);
-                                *output++ = buf++;
-                                oc++;
-                                if (++sentinel >= 3)
-                                    return; // non-convex polygon!
-                            }
-                            *output++ = p;
-                            oc++;
-                        } else {
-                           // both outside
-                        }
-                    }
-                    s = p;
-                    sd = pd;
-                }
-                // output list become the new input list
-                if (oc<3)
-                    return; // less than 3 vertices left? we're done!
-                ivl = ovl;
-                ic = oc;
-                outi = 1-outi;
-            }
-            cc >>= 1;
-            plane++;
-        } while (cc);
-    }
-    
-    // finally we can render our triangles...
-    p0 = ivl[0];
-    p1 = ivl[1];
-    for (unsigned int i=2 ; i<ic ; i++) {
-        p2 = ivl[i];
-        c->lerp.initTriangle(p0, p1, p2);
-        if (cull_triangle(c, p0, p1, p2)) {
-            p1 = p2;
-            continue; // culled!
-        }
-        triangle(c, p0, p1, p2);
-        p1 = p2;
-    }
-}
-
-unsigned int clip_line(ogles_context_t* c, vertex_t* s, vertex_t* p)
-{
-    const uint32_t all_cc = (s->flags | p->flags) & vertex_t::CLIP_ALL;
-
-    if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL))
-    {
-        unsigned int plane = 0;
-        uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8;
-        do {
-            if (cc & 1) {
-                const vec4_t& equation = c->clipPlanes.plane[plane].equation;
-                const GLfixed sd = dot4(equation.v, s->eye.v);
-                const GLfixed pd = dot4(equation.v, p->eye.v);
-                if (sd >= 0) {
-                    if (pd >= 0) {
-                        // both inside
-                    } else {
-                        // s inside, p outside (exiting)
-                        const GLfixed t = clipDivide(sd, sd-pd);
-                        c->arrays.clipEye(c, p, t, p, s);
-                    }
-                } else {
-                    if (pd >= 0) {
-                        // s outside (entering)
-                        if (pd) {
-                            const GLfixed t = clipDivide(pd, pd-sd);
-                            c->arrays.clipEye(c, s, t, s, p);
-                        }
-                    } else {
-                       // both outside
-                       return 0;
-                    }
-                }
-            }
-            cc >>= 1;
-            plane++;
-        } while (cc);
-    }
-
-    // frustum clip-planes
-    if (all_cc & vertex_t::FRUSTUM_CLIP_ALL)
-    {
-        unsigned int plane = 0;
-        uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL;
-        do {
-            if (cc & 1) {
-                const GLfixed sd = frustumPlaneDist(plane, s->clip);
-                const GLfixed pd = frustumPlaneDist(plane, p->clip);
-                if (sd >= 0) {
-                    if (pd >= 0) {
-                        // both inside
-                    } else {
-                        // s inside, p outside (exiting)
-                        const GLfixed t = clipDivide(sd, sd-pd);
-                        c->arrays.clipVertex(c, p, t, p, s);
-                    }
-                } else {
-                    if (pd >= 0) {
-                        // s outside (entering)
-                        if (pd) {
-                            const GLfixed t = clipDivide(pd, pd-sd);
-                            c->arrays.clipVertex(c, s, t, s, p);
-                        }
-                    } else {
-                       // both outside
-                       return 0;
-                    }
-                }
-            }
-            cc >>= 1;
-            plane++;
-        } while (cc);
-    }
-
-    return 2;
-}
-
-
-}; // namespace android
diff --git a/opengl/libagl/primitives.h b/opengl/libagl/primitives.h
deleted file mode 100644
index 1bef604..0000000
--- a/opengl/libagl/primitives.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* libs/opengles/primitives.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_PRIMITIVES_H
-#define ANDROID_OPENGLES_PRIMITIVES_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-
-namespace android {
-
-namespace gl {
-struct ogles_context_t;
-};
-
-void ogles_validate_primitives(ogles_context_t* c);
-
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_PRIMITIVES_H
-
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
deleted file mode 100644
index 8bb7e83..0000000
--- a/opengl/libagl/state.cpp
+++ /dev/null
@@ -1,598 +0,0 @@
-/* libs/opengles/state.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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 <stdlib.h>
-
-#include "context.h"
-#include "fp.h"
-#include "state.h"
-#include "array.h"
-#include "matrix.h"
-#include "vertex.h"
-#include "light.h"
-#include "texture.h"
-#include "BufferObjectManager.h"
-#include "TextureObjectManager.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static char const * const gVendorString     = "Android";
-static char const * const gRendererString   = "Android PixelFlinger 1.4";
-static char const * const gVersionString    = "OpenGL ES-CM 1.0";
-static char const * const gExtensionsString =
-    "GL_OES_byte_coordinates "              // OK
-    "GL_OES_fixed_point "                   // OK
-    "GL_OES_single_precision "              // OK
-    "GL_OES_read_format "                   // OK
-    "GL_OES_compressed_paletted_texture "   // OK
-    "GL_OES_draw_texture "                  // OK
-    "GL_OES_matrix_get "                    // OK
-    "GL_OES_query_matrix "                  // OK
-    //        "GL_OES_point_size_array "              // TODO
-    //        "GL_OES_point_sprite "                  // TODO
-    "GL_OES_EGL_image "                     // OK
-    "GL_OES_EGL_sync "                      // OK
-#ifdef GL_OES_compressed_ETC1_RGB8_texture
-    "GL_OES_compressed_ETC1_RGB8_texture "  // OK
-#endif
-    "GL_ARB_texture_compression "           // OK
-    "GL_ARB_texture_non_power_of_two "      // OK
-    "GL_ANDROID_user_clip_plane "           // OK
-    "GL_ANDROID_vertex_buffer_object "      // OK
-    "GL_ANDROID_generate_mipmap "           // OK
-    ;
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-ogles_context_t *ogles_init(size_t extra)
-{
-    void* const base = malloc(extra + sizeof(ogles_context_t) + 32);
-    if (!base) return 0;
-
-    ogles_context_t *c =
-            (ogles_context_t *)((ptrdiff_t(base) + extra + 31) & ~0x1FL);
-    memset(c, 0, sizeof(ogles_context_t));
-    ggl_init_context(&(c->rasterizer));
-
-    // XXX: this should be passed as an argument
-    sp<EGLSurfaceManager> smgr(new EGLSurfaceManager());
-    c->surfaceManager = smgr.get();
-    c->surfaceManager->incStrong(c);
-
-    sp<EGLBufferObjectManager> bomgr(new EGLBufferObjectManager());
-    c->bufferObjectManager = bomgr.get();
-    c->bufferObjectManager->incStrong(c);
-
-    ogles_init_array(c);
-    ogles_init_matrix(c);
-    ogles_init_vertex(c);
-    ogles_init_light(c);
-    ogles_init_texture(c);
-
-    c->rasterizer.base = base;
-    c->point.size = TRI_ONE;
-    c->line.width = TRI_ONE;
-
-    // in OpenGL, writing to the depth buffer is enabled by default.
-    c->rasterizer.procs.depthMask(c, 1);
-
-    // OpenGL enables dithering by default
-    c->rasterizer.procs.enable(c, GL_DITHER);
-
-    return c;
-}
-
-void ogles_uninit(ogles_context_t* c)
-{
-    ogles_uninit_array(c);
-    ogles_uninit_matrix(c);
-    ogles_uninit_vertex(c);
-    ogles_uninit_light(c);
-    ogles_uninit_texture(c);
-    c->surfaceManager->decStrong(c);
-    c->bufferObjectManager->decStrong(c);
-    ggl_uninit_context(&(c->rasterizer));
-    free(c->rasterizer.base);
-}
-
-void _ogles_error(ogles_context_t* c, GLenum error)
-{
-    if (c->error == GL_NO_ERROR)
-        c->error = error;
-}
-
-static bool stencilop_valid(GLenum op) {
-    switch (op) {
-    case GL_KEEP:
-    case GL_ZERO:
-    case GL_REPLACE:
-    case GL_INCR:
-    case GL_DECR:
-    case GL_INVERT:
-        return true;
-    }
-    return false;
-}
-
-static void enable_disable(ogles_context_t* c, GLenum cap, int enabled)
-{
-    if ((cap >= GL_LIGHT0) && (cap<GL_LIGHT0+OGLES_MAX_LIGHTS)) {
-        c->lighting.lights[cap-GL_LIGHT0].enable = enabled;
-        c->lighting.enabledLights &= ~(1<<(cap-GL_LIGHT0));
-        c->lighting.enabledLights |= (enabled<<(cap-GL_LIGHT0));
-        return;
-    }
-
-    switch (cap) {
-    case GL_POINT_SMOOTH:
-        c->point.smooth = enabled;
-        break;
-    case GL_LINE_SMOOTH:
-        c->line.smooth = enabled;
-        break;
-    case GL_POLYGON_OFFSET_FILL:
-        c->polygonOffset.enable = enabled;
-        break;
-    case GL_CULL_FACE:
-        c->cull.enable = enabled;
-        break;
-    case GL_LIGHTING:
-        c->lighting.enable = enabled;
-        break;
-    case GL_COLOR_MATERIAL:
-        c->lighting.colorMaterial.enable = enabled;
-        break;
-    case GL_NORMALIZE:
-    case GL_RESCALE_NORMAL:
-        c->transforms.rescaleNormals = enabled ? cap : 0;
-        // XXX: invalidate mvit
-        break;
-
-    case GL_CLIP_PLANE0:
-    case GL_CLIP_PLANE1:
-    case GL_CLIP_PLANE2:
-    case GL_CLIP_PLANE3:
-    case GL_CLIP_PLANE4:
-    case GL_CLIP_PLANE5:
-        c->clipPlanes.enable &= ~(1<<(cap-GL_CLIP_PLANE0));
-        c->clipPlanes.enable |= (enabled<<(cap-GL_CLIP_PLANE0));
-        ogles_invalidate_perspective(c);
-        break;
-
-    case GL_FOG:
-    case GL_DEPTH_TEST:
-        ogles_invalidate_perspective(c);
-        [[fallthrough]];
-    case GL_BLEND:
-    case GL_SCISSOR_TEST:
-    case GL_ALPHA_TEST:
-    case GL_COLOR_LOGIC_OP:
-    case GL_DITHER:
-    case GL_STENCIL_TEST:
-    case GL_TEXTURE_2D:
-        // these need to fall through into the rasterizer
-        c->rasterizer.procs.enableDisable(c, cap, enabled);
-        break;
-    case GL_TEXTURE_EXTERNAL_OES:
-        c->rasterizer.procs.enableDisable(c, GL_TEXTURE_2D, enabled);
-        break;
-
-    case GL_MULTISAMPLE:
-    case GL_SAMPLE_ALPHA_TO_COVERAGE:
-    case GL_SAMPLE_ALPHA_TO_ONE:
-    case GL_SAMPLE_COVERAGE:
-        // not supported in this implementation
-        break;
-
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-using namespace android;
-
-#if 0
-#pragma mark -
-#endif
-
-// These ones are super-easy, we're not supporting those features!
-void glSampleCoverage(GLclampf /*value*/, GLboolean /*invert*/) {
-}
-void glSampleCoveragex(GLclampx /*value*/, GLboolean /*invert*/) {
-}
-void glStencilFunc(GLenum func, GLint /*ref*/, GLuint /*mask*/) {
-    ogles_context_t* c = ogles_context_t::get();
-    if (func < GL_NEVER || func > GL_ALWAYS) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    // from OpenGL|ES 1.0 sepcification:
-    // If there is no stencil buffer, no stencil modification can occur
-    // and it is as if the stencil test always passes.
-}
-
-void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
-    ogles_context_t* c = ogles_context_t::get();
-    if ((stencilop_valid(fail) &
-         stencilop_valid(zfail) &
-         stencilop_valid(zpass)) == 0) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-void glAlphaFunc(GLenum func, GLclampf ref)
-{
-    glAlphaFuncx(func, gglFloatToFixed(ref));
-}
-
-void glCullFace(GLenum mode)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    switch (mode) {
-    case GL_FRONT:
-    case GL_BACK:
-    case GL_FRONT_AND_BACK:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-    }
-    c->cull.cullFace = mode;
-}
-
-void glFrontFace(GLenum mode)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    switch (mode) {
-    case GL_CW:
-    case GL_CCW:
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->cull.frontFace = mode;
-}
-
-void glHint(GLenum target, GLenum mode)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    switch (target) {
-    case GL_FOG_HINT:
-    case GL_GENERATE_MIPMAP_HINT:
-    case GL_LINE_SMOOTH_HINT:
-        break;
-    case GL_POINT_SMOOTH_HINT:
-        c->rasterizer.procs.enableDisable(c,
-                GGL_POINT_SMOOTH_NICE, mode==GL_NICEST);
-        break;
-    case GL_PERSPECTIVE_CORRECTION_HINT:
-        c->perspective = (mode == GL_NICEST) ? 1 : 0;
-        break;
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-    }
-}
-
-void glEnable(GLenum cap) {
-    ogles_context_t* c = ogles_context_t::get();
-    enable_disable(c, cap, 1);
-}
-void glDisable(GLenum cap) {
-    ogles_context_t* c = ogles_context_t::get();
-    enable_disable(c, cap, 0);
-}
-
-void glFinish()
-{ // nothing to do for our software implementation
-}
-
-void glFlush()
-{ // nothing to do for our software implementation
-}
-
-GLenum glGetError()
-{
-    // From OpenGL|ES 1.0 specification:
-    // If more than one flag has recorded an error, glGetError returns
-    // and clears an arbitrary error flag value. Thus, glGetError should
-    // always be called in a loop, until it returns GL_NO_ERROR,
-    // if all error flags are to be reset.
-
-    ogles_context_t* c = ogles_context_t::get();
-    if (c->error) {
-        const GLenum ret(c->error);
-        c->error = 0;
-        return ret;
-    }
-
-    if (c->rasterizer.error) {
-        const GLenum ret(c->rasterizer.error);
-        c->rasterizer.error = 0;
-        return ret;
-    }
-
-    return GL_NO_ERROR;
-}
-
-const GLubyte* glGetString(GLenum string)
-{
-    switch (string) {
-    case GL_VENDOR:     return (const GLubyte*)gVendorString;
-    case GL_RENDERER:   return (const GLubyte*)gRendererString;
-    case GL_VERSION:    return (const GLubyte*)gVersionString;
-    case GL_EXTENSIONS: return (const GLubyte*)gExtensionsString;
-    }
-    ogles_context_t* c = ogles_context_t::get();
-    ogles_error(c, GL_INVALID_ENUM);
-    return 0;
-}
-
-void glGetIntegerv(GLenum pname, GLint *params)
-{
-    int i;
-    ogles_context_t* c = ogles_context_t::get();
-    switch (pname) {
-    case GL_ALIASED_POINT_SIZE_RANGE:
-        params[0] = 0;
-        params[1] = GGL_MAX_ALIASED_POINT_SIZE;
-        break;
-    case GL_ALIASED_LINE_WIDTH_RANGE:
-        params[0] = 0;
-        params[1] = GGL_MAX_ALIASED_POINT_SIZE;
-        break;
-    case GL_ALPHA_BITS: {
-        int index = c->rasterizer.state.buffers.color.format;
-        GGLFormat const * formats = gglGetPixelFormatTable();
-        params[0] = formats[index].ah - formats[index].al;
-        break;
-        }
-    case GL_RED_BITS: {
-        int index = c->rasterizer.state.buffers.color.format;
-        GGLFormat const * formats = gglGetPixelFormatTable();
-        params[0] = formats[index].rh - formats[index].rl;
-        break;
-        }
-    case GL_GREEN_BITS: {
-        int index = c->rasterizer.state.buffers.color.format;
-        GGLFormat const * formats = gglGetPixelFormatTable();
-        params[0] = formats[index].gh - formats[index].gl;
-        break;
-        }
-    case GL_BLUE_BITS: {
-        int index = c->rasterizer.state.buffers.color.format;
-        GGLFormat const * formats = gglGetPixelFormatTable();
-        params[0] = formats[index].bh - formats[index].bl;
-        break;
-        }
-    case GL_COMPRESSED_TEXTURE_FORMATS:
-        params[ 0] = GL_PALETTE4_RGB8_OES;
-        params[ 1] = GL_PALETTE4_RGBA8_OES;
-        params[ 2] = GL_PALETTE4_R5_G6_B5_OES;
-        params[ 3] = GL_PALETTE4_RGBA4_OES;
-        params[ 4] = GL_PALETTE4_RGB5_A1_OES;
-        params[ 5] = GL_PALETTE8_RGB8_OES;
-        params[ 6] = GL_PALETTE8_RGBA8_OES;
-        params[ 7] = GL_PALETTE8_R5_G6_B5_OES;
-        params[ 8] = GL_PALETTE8_RGBA4_OES;
-        params[ 9] = GL_PALETTE8_RGB5_A1_OES;
-        i = 10;
-#ifdef GL_OES_compressed_ETC1_RGB8_texture
-        params[i++] = GL_ETC1_RGB8_OES;
-#endif
-        break;
-    case GL_DEPTH_BITS:
-        params[0] = c->rasterizer.state.buffers.depth.format ? 0 : 16;
-        break;
-    case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES:
-        params[0] = GL_RGB;
-        break;
-    case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES:
-        params[0] = GL_UNSIGNED_SHORT_5_6_5;
-        break;
-    case GL_MAX_LIGHTS:
-        params[0] = OGLES_MAX_LIGHTS;
-        break;
-    case GL_MAX_CLIP_PLANES:
-        params[0] = OGLES_MAX_CLIP_PLANES;
-        break;
-    case GL_MAX_MODELVIEW_STACK_DEPTH:
-        params[0] = OGLES_MODELVIEW_STACK_DEPTH;
-        break;
-    case GL_MAX_PROJECTION_STACK_DEPTH:
-        params[0] = OGLES_PROJECTION_STACK_DEPTH;
-        break;
-    case GL_MAX_TEXTURE_STACK_DEPTH:
-        params[0] = OGLES_TEXTURE_STACK_DEPTH;
-        break;
-    case GL_MAX_TEXTURE_SIZE:
-        params[0] = GGL_MAX_TEXTURE_SIZE;
-        break;
-    case GL_MAX_TEXTURE_UNITS:
-        params[0] = GGL_TEXTURE_UNIT_COUNT;
-        break;
-    case GL_MAX_VIEWPORT_DIMS:
-        params[0] = GGL_MAX_VIEWPORT_DIMS;
-        params[1] = GGL_MAX_VIEWPORT_DIMS;
-        break;
-    case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
-        params[0] = OGLES_NUM_COMPRESSED_TEXTURE_FORMATS;
-        break;
-    case GL_SMOOTH_LINE_WIDTH_RANGE:
-        params[0] = 0;
-        params[1] = GGL_MAX_SMOOTH_LINE_WIDTH;
-        break;
-    case GL_SMOOTH_POINT_SIZE_RANGE:
-        params[0] = 0;
-        params[1] = GGL_MAX_SMOOTH_POINT_SIZE;
-        break;
-    case GL_STENCIL_BITS:
-        params[0] = 0;
-        break;
-    case GL_SUBPIXEL_BITS:
-        params[0] = GGL_SUBPIXEL_BITS;
-        break;
-
-    case GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES:
-        memcpy( params,
-                c->transforms.modelview.top().elements(),
-                16*sizeof(GLint));
-        break;
-    case GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES:
-        memcpy( params,
-                c->transforms.projection.top().elements(),
-                16*sizeof(GLint));
-        break;
-    case GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES:
-        memcpy( params,
-                c->transforms.texture[c->textures.active].top().elements(),
-                16*sizeof(GLint));
-        break;
-
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        break;
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-void glPointSize(GLfloat size)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (size <= 0) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->point.size = TRI_FROM_FIXED(gglFloatToFixed(size));
-}
-
-void glPointSizex(GLfixed size)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (size <= 0) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->point.size = TRI_FROM_FIXED(size);
-}
-
-// ----------------------------------------------------------------------------
-
-void glLineWidth(GLfloat width)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (width <= 0) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->line.width = TRI_FROM_FIXED(gglFloatToFixed(width));
-}
-
-void glLineWidthx(GLfixed width)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (width <= 0) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->line.width = TRI_FROM_FIXED(width);
-}
-
-// ----------------------------------------------------------------------------
-
-void glColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.colorMask(c, r, g, b, a);
-}
-
-void glDepthMask(GLboolean flag) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.depthMask(c, flag);
-}
-
-void glStencilMask(GLuint mask) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.stencilMask(c, mask);
-}
-
-void glDepthFunc(GLenum func) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.depthFunc(c, func);
-}
-
-void glLogicOp(GLenum opcode) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.logicOp(c, opcode);
-}
-
-void glAlphaFuncx(GLenum func, GLclampx ref) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.alphaFuncx(c, func, ref);
-}
-
-void glBlendFunc(GLenum sfactor, GLenum dfactor) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.blendFunc(c, sfactor, dfactor);
-}
-
-void glClear(GLbitfield mask) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.clear(c, mask);
-}
-
-void glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.clearColorx(c, red, green, blue, alpha);
-}
-
-void glClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.clearColorx(c,
-                    gglFloatToFixed(r),
-                    gglFloatToFixed(g),
-                    gglFloatToFixed(b),
-                    gglFloatToFixed(a));
-}
-
-void glClearDepthx(GLclampx depth) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.clearDepthx(c, depth);
-}
-
-void glClearDepthf(GLclampf depth)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.clearDepthx(c, gglFloatToFixed(depth));
-}
-
-void glClearStencil(GLint s) {
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.clearStencil(c, s);
-}
diff --git a/opengl/libagl/state.h b/opengl/libagl/state.h
deleted file mode 100644
index 55a5ccb..0000000
--- a/opengl/libagl/state.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* libs/opengles/state.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_STATE_H
-#define ANDROID_OPENGLES_STATE_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-#include <private/pixelflinger/ggl_context.h>
-
-#include <GLES/gl.h>
-
-#include <stdio.h>
-
-namespace android {
-
-ogles_context_t *ogles_init(size_t extra);
-void ogles_uninit(ogles_context_t* c);
-void _ogles_error(ogles_context_t* c, GLenum error);
-
-#ifndef TRACE_GL_ERRORS
-#define TRACE_GL_ERRORS 0
-#endif
-
-#if TRACE_GL_ERRORS
-#define ogles_error(c, error) \
-do { \
-  printf("ogles_error at file %s line %d\n", __FILE__, __LINE__); \
-  _ogles_error(c, error); \
-} while (0)
-#else /* !TRACE_GL_ERRORS */
-#define ogles_error(c, error) _ogles_error((c), (error))
-#endif
-
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_STATE_H
-
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
deleted file mode 100644
index 4c5f3e9..0000000
--- a/opengl/libagl/texture.cpp
+++ /dev/null
@@ -1,1643 +0,0 @@
-/* libs/opengles/texture.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT 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 <stdio.h>
-#include <stdlib.h>
-#include "context.h"
-#include "fp.h"
-#include "state.h"
-#include "texture.h"
-#include "TextureObjectManager.h"
-
-#include <ETC1/etc1.h>
-
-#include <ui/GraphicBufferMapper.h>
-#include <ui/Rect.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-static void bindTextureTmu(
-    ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex);
-
-static __attribute__((noinline))
-void generateMipmap(ogles_context_t* c, GLint level);
-
-// ----------------------------------------------------------------------------
-
-#if 0
-#pragma mark -
-#pragma mark Init
-#endif
-
-void ogles_init_texture(ogles_context_t* c)
-{
-    c->textures.packAlignment   = 4;
-    c->textures.unpackAlignment = 4;
-
-    // each context has a default named (0) texture (not shared)
-    c->textures.defaultTexture = new EGLTextureObject();
-    c->textures.defaultTexture->incStrong(c);
-
-    // bind the default texture to each texture unit
-    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        bindTextureTmu(c, i, 0, c->textures.defaultTexture);
-        memset(c->current.texture[i].v, 0, sizeof(vec4_t));
-        c->current.texture[i].Q = 0x10000;
-    }
-}
-
-void ogles_uninit_texture(ogles_context_t* c)
-{
-    if (c->textures.ggl)
-        gglUninit(c->textures.ggl);
-    c->textures.defaultTexture->decStrong(c);
-    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        if (c->textures.tmu[i].texture)
-            c->textures.tmu[i].texture->decStrong(c);
-    }
-}
-
-static __attribute__((noinline))
-void validate_tmu(ogles_context_t* c, int i)
-{
-    texture_unit_t& u(c->textures.tmu[i]);
-    if (u.dirty) {
-        u.dirty = 0;
-        c->rasterizer.procs.activeTexture(c, i);
-        c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
-        c->rasterizer.procs.texGeni(c, GGL_S,
-                GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
-        c->rasterizer.procs.texGeni(c, GGL_T,
-                GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
-        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
-                GGL_TEXTURE_WRAP_S, u.texture->wraps);
-        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
-                GGL_TEXTURE_WRAP_T, u.texture->wrapt);
-        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
-                GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
-        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
-                GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
-
-        // disable this texture unit if it's not complete
-        if (!u.texture->isComplete()) {
-            c->rasterizer.procs.disable(c, GGL_TEXTURE_2D);
-        }
-    }
-}
-
-void ogles_validate_texture(ogles_context_t* c)
-{
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        if (c->rasterizer.state.texture[i].enable)
-            validate_tmu(c, i);
-    }
-    c->rasterizer.procs.activeTexture(c, c->textures.active);
-}
-
-static
-void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) {
-    c->textures.tmu[tmu].dirty = flags;
-}
-
-/*
- * If the active textures are EGLImage, they need to be locked before
- * they can be used.
- *
- * FIXME: code below is far from being optimal
- *
- */
-
-void ogles_lock_textures(ogles_context_t* c)
-{
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        if (c->rasterizer.state.texture[i].enable) {
-            texture_unit_t& u(c->textures.tmu[i]);
-            ANativeWindowBuffer* native_buffer = u.texture->buffer;
-            if (native_buffer) {
-                c->rasterizer.procs.activeTexture(c, i);
-
-                auto& mapper = GraphicBufferMapper::get();
-                void* vaddr;
-                mapper.lock(native_buffer->handle, GRALLOC_USAGE_SW_READ_OFTEN,
-                        Rect(native_buffer->width, native_buffer->height),
-                        &vaddr);
-
-                u.texture->setImageBits(vaddr);
-                c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
-            }
-        }
-    }
-}
-
-void ogles_unlock_textures(ogles_context_t* c)
-{
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        if (c->rasterizer.state.texture[i].enable) {
-            texture_unit_t& u(c->textures.tmu[i]);
-            ANativeWindowBuffer* native_buffer = u.texture->buffer;
-            if (native_buffer) {
-                c->rasterizer.procs.activeTexture(c, i);
-
-                auto& mapper = GraphicBufferMapper::get();
-                mapper.unlock(native_buffer->handle);
-
-                u.texture->setImageBits(NULL);
-                c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
-            }
-        }
-    }
-    c->rasterizer.procs.activeTexture(c, c->textures.active);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Format conversion
-#endif
-
-static uint32_t gl2format_table[6][4] = {
-    // BYTE, 565, 4444, 5551
-    { GGL_PIXEL_FORMAT_A_8,
-      0, 0, 0 },                        // GL_ALPHA
-    { GGL_PIXEL_FORMAT_RGB_888,
-      GGL_PIXEL_FORMAT_RGB_565,
-      0, 0 },                           // GL_RGB
-    { GGL_PIXEL_FORMAT_RGBA_8888,
-      0,
-      GGL_PIXEL_FORMAT_RGBA_4444,
-      GGL_PIXEL_FORMAT_RGBA_5551 },     // GL_RGBA
-    { GGL_PIXEL_FORMAT_L_8,
-      0, 0, 0 },                        // GL_LUMINANCE
-    { GGL_PIXEL_FORMAT_LA_88,
-      0, 0, 0 },                        // GL_LUMINANCE_ALPHA
-};
-
-static int32_t convertGLPixelFormat(GLint format, GLenum type)
-{
-    int32_t fi = -1;
-    int32_t ti = -1;
-    switch (format) {
-    case GL_ALPHA:              fi = 0;     break;
-    case GL_RGB:                fi = 1;     break;
-    case GL_RGBA:               fi = 2;     break;
-    case GL_LUMINANCE:          fi = 3;     break;
-    case GL_LUMINANCE_ALPHA:    fi = 4;     break;
-    }
-    switch (type) {
-    case GL_UNSIGNED_BYTE:          ti = 0; break;
-    case GL_UNSIGNED_SHORT_5_6_5:   ti = 1; break;
-    case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break;
-    case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break;
-    }
-    if (fi==-1 || ti==-1)
-        return 0;
-    return gl2format_table[fi][ti];
-}
-
-// ----------------------------------------------------------------------------
-
-static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type)
-{
-    GLenum error = 0;
-    if (format<GL_ALPHA || format>GL_LUMINANCE_ALPHA) {
-        error = GL_INVALID_ENUM;
-    }
-    if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 &&
-        type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) {
-        error = GL_INVALID_ENUM;
-    }
-    if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) {
-        error = GL_INVALID_OPERATION;
-    }
-    if ((type == GL_UNSIGNED_SHORT_4_4_4_4 ||
-         type == GL_UNSIGNED_SHORT_5_5_5_1)  && format != GL_RGBA) {
-        error = GL_INVALID_OPERATION;
-    }
-    if (error) {
-        ogles_error(c, error);
-    }
-    return error;
-}
-
-// ----------------------------------------------------------------------------
-
-GGLContext* getRasterizer(ogles_context_t* c)
-{
-    GGLContext* ggl = c->textures.ggl;
-    if (ggl_unlikely(!ggl)) {
-        // this is quite heavy the first time...
-        gglInit(&ggl);
-        if (!ggl) {
-            return 0;
-        }
-        GGLfixed colors[4] = { 0, 0, 0, 0x10000 };
-        c->textures.ggl = ggl;
-        ggl->activeTexture(ggl, 0);
-        ggl->enable(ggl, GGL_TEXTURE_2D);
-        ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
-        ggl->disable(ggl, GGL_DITHER);
-        ggl->shadeModel(ggl, GGL_FLAT);
-        ggl->color4xv(ggl, colors);
-    }
-    return ggl;
-}
-
-static __attribute__((noinline))
-int copyPixels(
-        ogles_context_t* c,
-        const GGLSurface& dst,
-        GLint xoffset, GLint yoffset,
-        const GGLSurface& src,
-        GLint x, GLint y, GLsizei w, GLsizei h)
-{
-    if ((dst.format == src.format) &&
-        (dst.stride == src.stride) &&
-        (dst.width == src.width) &&
-        (dst.height == src.height) &&
-        (dst.stride > 0) &&
-        ((x|y) == 0) &&
-        ((xoffset|yoffset) == 0))
-    {
-        // this is a common case...
-        const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]);
-        const size_t size = src.height * src.stride * pixelFormat.size;
-        memcpy(dst.data, src.data, size);
-        return 0;
-    }
-
-    // use pixel-flinger to handle all the conversions
-    GGLContext* ggl = getRasterizer(c);
-    if (!ggl) {
-        // the only reason this would fail is because we ran out of memory
-        return GL_OUT_OF_MEMORY;
-    }
-
-    ggl->colorBuffer(ggl, &dst);
-    ggl->bindTexture(ggl, &src);
-    ggl->texCoord2i(ggl, x-xoffset, y-yoffset);
-    ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h);
-    return 0;
-}
-
-// ----------------------------------------------------------------------------
-
-static __attribute__((noinline))
-sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c)
-{
-    sp<EGLTextureObject> tex;
-    const int active = c->textures.active;
-    const GLuint name = c->textures.tmu[active].name;
-
-    // free the reference to the previously bound object
-    texture_unit_t& u(c->textures.tmu[active]);
-    if (u.texture)
-        u.texture->decStrong(c);
-
-    if (name == 0) {
-        // 0 is our local texture object, not shared with anyone.
-        // But it affects all bound TMUs immediately.
-        // (we need to invalidate all units bound to this texture object)
-        tex = c->textures.defaultTexture;
-        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-            if (c->textures.tmu[i].texture == tex.get())
-                invalidate_texture(c, i);
-        }
-    } else {
-        // get a new texture object for that name
-        tex = c->surfaceManager->replaceTexture(name);
-    }
-
-    // bind this texture to the current active texture unit
-    // and add a reference to this texture object
-    u.texture = tex.get();
-    u.texture->incStrong(c);
-    u.name = name;
-    invalidate_texture(c, active);
-    return tex;
-}
-
-void bindTextureTmu(
-    ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex)
-{
-    if (tex.get() == c->textures.tmu[tmu].texture)
-        return;
-
-    // free the reference to the previously bound object
-    texture_unit_t& u(c->textures.tmu[tmu]);
-    if (u.texture)
-        u.texture->decStrong(c);
-
-    // bind this texture to the current active texture unit
-    // and add a reference to this texture object
-    u.texture = tex.get();
-    u.texture->incStrong(c);
-    u.name = texture;
-    invalidate_texture(c, tmu);
-}
-
-int createTextureSurface(ogles_context_t* c,
-        GGLSurface** outSurface, int32_t* outSize, GLint level,
-        GLenum format, GLenum type, GLsizei width, GLsizei height,
-        GLenum compressedFormat = 0)
-{
-    // convert the pixelformat to one we can handle
-    const int32_t formatIdx = convertGLPixelFormat(format, type);
-    if (formatIdx == 0) { // we don't know what to do with this
-        return GL_INVALID_OPERATION;
-    }
-
-    // figure out the size we need as well as the stride
-    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
-    const int32_t align = c->textures.unpackAlignment-1;
-    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
-    const size_t size = bpr * height;
-    const int32_t stride = bpr / pixelFormat.size;
-
-    if (level > 0) {
-        const int active = c->textures.active;
-        EGLTextureObject* tex = c->textures.tmu[active].texture;
-        status_t err = tex->reallocate(level,
-                width, height, stride, formatIdx, compressedFormat, bpr);
-        if (err != NO_ERROR)
-            return GL_OUT_OF_MEMORY;
-        GGLSurface& surface = tex->editMip(level);
-        *outSurface = &surface;
-        *outSize = size;
-        return 0;
-    }
-
-    sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
-    status_t err = tex->reallocate(level,
-            width, height, stride, formatIdx, compressedFormat, bpr);
-    if (err != NO_ERROR)
-        return GL_OUT_OF_MEMORY;
-
-    tex->internalformat = format;
-    *outSurface = &tex->surface;
-    *outSize = size;
-    return 0;
-}
-
-static GLsizei dataSizePalette4(int numLevels, int width, int height, int format)
-{
-    int indexBits = 8;
-    int entrySize = 0;
-    switch (format) {
-    case GL_PALETTE4_RGB8_OES:
-        indexBits = 4;
-        [[fallthrough]];
-    case GL_PALETTE8_RGB8_OES:
-        entrySize = 3;
-        break;
-
-    case GL_PALETTE4_RGBA8_OES:
-        indexBits = 4;
-        [[fallthrough]];
-    case GL_PALETTE8_RGBA8_OES:
-        entrySize = 4;
-        break;
-
-    case GL_PALETTE4_R5_G6_B5_OES:
-    case GL_PALETTE4_RGBA4_OES:
-    case GL_PALETTE4_RGB5_A1_OES:
-        indexBits = 4;
-        [[fallthrough]];
-    case GL_PALETTE8_R5_G6_B5_OES:
-    case GL_PALETTE8_RGBA4_OES:
-    case GL_PALETTE8_RGB5_A1_OES:
-        entrySize = 2;
-        break;
-    }
-
-    size_t size = (1 << indexBits) * entrySize; // palette size
-
-    for (int i=0 ; i< numLevels ; i++) {
-        int w = (width  >> i) ? : 1;
-        int h = (height >> i) ? : 1;
-        int levelSize = h * ((w * indexBits) / 8) ? : 1;
-        size += levelSize;
-    }
-
-    return size;
-}
-
-static void decodePalette4(const GLvoid *data, int level, int width, int height,
-                           void *surface, int stride, int format)
-
-{
-    int indexBits = 8;
-    int entrySize = 0;
-    switch (format) {
-    case GL_PALETTE4_RGB8_OES:
-        indexBits = 4;
-        [[fallthrough]];
-    case GL_PALETTE8_RGB8_OES:
-        entrySize = 3;
-        break;
-
-    case GL_PALETTE4_RGBA8_OES:
-        indexBits = 4;
-        [[fallthrough]];
-    case GL_PALETTE8_RGBA8_OES:
-        entrySize = 4;
-        break;
-
-    case GL_PALETTE4_R5_G6_B5_OES:
-    case GL_PALETTE4_RGBA4_OES:
-    case GL_PALETTE4_RGB5_A1_OES:
-        indexBits = 4;
-        [[fallthrough]];
-    case GL_PALETTE8_R5_G6_B5_OES:
-    case GL_PALETTE8_RGBA4_OES:
-    case GL_PALETTE8_RGB5_A1_OES:
-        entrySize = 2;
-        break;
-    }
-
-    const int paletteSize = (1 << indexBits) * entrySize;
-
-    uint8_t const* pixels = (uint8_t *)data + paletteSize;
-    for (int i=0 ; i<level ; i++) {
-        int w = (width  >> i) ? : 1;
-        int h = (height >> i) ? : 1;
-        pixels += h * ((w * indexBits) / 8);
-    }
-    width  = (width  >> level) ? : 1;
-    height = (height >> level) ? : 1;
-
-    if (entrySize == 2) {
-        uint8_t const* const palette = (uint8_t*)data;
-        for (int y=0 ; y<height ; y++) {
-            uint8_t* p = (uint8_t*)surface + y*stride*2;
-            if (indexBits == 8) {
-                for (int x=0 ; x<width ; x++) {
-                    int index = 2 * (*pixels++);
-                    *p++ = palette[index + 0];
-                    *p++ = palette[index + 1];
-                }
-            } else {
-                for (int x=0 ; x<width ; x+=2) {
-                    int v = *pixels++;
-                    int index = 2 * (v >> 4);
-                    *p++ = palette[index + 0];
-                    *p++ = palette[index + 1];
-                    if (x+1 < width) {
-                        index = 2 * (v & 0xF);
-                        *p++ = palette[index + 0];
-                        *p++ = palette[index + 1];
-                    }
-                }
-            }
-        }
-    } else if (entrySize == 3) {
-        uint8_t const* const palette = (uint8_t*)data;
-        for (int y=0 ; y<height ; y++) {
-            uint8_t* p = (uint8_t*)surface + y*stride*3;
-            if (indexBits == 8) {
-                for (int x=0 ; x<width ; x++) {
-                    int index = 3 * (*pixels++);
-                    *p++ = palette[index + 0];
-                    *p++ = palette[index + 1];
-                    *p++ = palette[index + 2];
-                }
-            } else {
-                for (int x=0 ; x<width ; x+=2) {
-                    int v = *pixels++;
-                    int index = 3 * (v >> 4);
-                    *p++ = palette[index + 0];
-                    *p++ = palette[index + 1];
-                    *p++ = palette[index + 2];
-                    if (x+1 < width) {
-                        index = 3 * (v & 0xF);
-                        *p++ = palette[index + 0];
-                        *p++ = palette[index + 1];
-                        *p++ = palette[index + 2];
-                    }
-                }
-            }
-        }
-    } else if (entrySize == 4) {
-        uint8_t const* const palette = (uint8_t*)data;
-        for (int y=0 ; y<height ; y++) {
-            uint8_t* p = (uint8_t*)surface + y*stride*4;
-            if (indexBits == 8) {
-                for (int x=0 ; x<width ; x++) {
-                    int index = 4 * (*pixels++);
-                    *p++ = palette[index + 0];
-                    *p++ = palette[index + 1];
-                    *p++ = palette[index + 2];
-                    *p++ = palette[index + 3];
-                }
-            } else {
-                for (int x=0 ; x<width ; x+=2) {
-                    int v = *pixels++;
-                    int index = 4 * (v >> 4);
-                    *p++ = palette[index + 0];
-                    *p++ = palette[index + 1];
-                    *p++ = palette[index + 2];
-                    *p++ = palette[index + 3];
-                    if (x+1 < width) {
-                        index = 4 * (v & 0xF);
-                        *p++ = palette[index + 0];
-                        *p++ = palette[index + 1];
-                        *p++ = palette[index + 2];
-                        *p++ = palette[index + 3];
-                    }
-                }
-            }
-        }
-    }
-}
-
-
-
-static __attribute__((noinline))
-void set_depth_and_fog(ogles_context_t* c, GGLfixed z)
-{
-    const uint32_t enables = c->rasterizer.state.enables;
-    // we need to compute Zw
-    int32_t iterators[3];
-    iterators[1] = iterators[2] = 0;
-    GGLfixed Zw;
-    GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear);
-    GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar);
-    if (z<=0)               Zw = n;
-    else if (z>=0x10000)    Zw = f;
-    else            Zw = gglMulAddx(z, (f-n), n);
-    if (enables & GGL_ENABLE_FOG) {
-        // set up fog if needed...
-        iterators[0] = c->fog.fog(c, Zw);
-        c->rasterizer.procs.fogGrad3xv(c, iterators);
-    }
-    if (enables & GGL_ENABLE_DEPTH_TEST) {
-        // set up z-test if needed...
-        int32_t z = (Zw & ~(Zw>>31));
-        if (z >= 0x10000)
-            z = 0xFFFF;
-        iterators[0] = (z << 16) | z;
-        c->rasterizer.procs.zGrad3xv(c, iterators);
-    }
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Generate mimaps
-#endif
-
-extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex);
-
-void generateMipmap(ogles_context_t* c, GLint level)
-{
-    if (level == 0) {
-        const int active = c->textures.active;
-        EGLTextureObject* tex = c->textures.tmu[active].texture;
-        if (tex->generate_mipmap) {
-            if (buildAPyramid(c, tex) != NO_ERROR) {
-                ogles_error(c, GL_OUT_OF_MEMORY);
-                return;
-            }
-        }
-    }
-}
-
-
-static void texParameterx(
-        GLenum target, GLenum pname, GLfixed param, ogles_context_t* c)
-{
-    if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
-    switch (pname) {
-    case GL_TEXTURE_WRAP_S:
-        if ((param == GL_REPEAT) ||
-            (param == GL_CLAMP_TO_EDGE)) {
-            textureObject->wraps = param;
-        } else {
-            goto invalid_enum;
-        }
-        break;
-    case GL_TEXTURE_WRAP_T:
-        if ((param == GL_REPEAT) ||
-            (param == GL_CLAMP_TO_EDGE)) {
-            textureObject->wrapt = param;
-        } else {
-            goto invalid_enum;
-        }
-        break;
-    case GL_TEXTURE_MIN_FILTER:
-        if ((param == GL_NEAREST) ||
-            (param == GL_LINEAR) ||
-            (param == GL_NEAREST_MIPMAP_NEAREST) ||
-            (param == GL_LINEAR_MIPMAP_NEAREST) ||
-            (param == GL_NEAREST_MIPMAP_LINEAR) ||
-            (param == GL_LINEAR_MIPMAP_LINEAR)) {
-            textureObject->min_filter = param;
-        } else {
-            goto invalid_enum;
-        }
-        break;
-    case GL_TEXTURE_MAG_FILTER:
-        if ((param == GL_NEAREST) ||
-            (param == GL_LINEAR)) {
-            textureObject->mag_filter = param;
-        } else {
-            goto invalid_enum;
-        }
-        break;
-    case GL_GENERATE_MIPMAP:
-        textureObject->generate_mipmap = param;
-        break;
-    default:
-invalid_enum:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    invalidate_texture(c, c->textures.active);
-}
-
-
-
-static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
-        ogles_context_t* c)
-{
-    ogles_lock_textures(c);
-
-    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
-    y = gglIntToFixed(cbSurface.height) - (y + h);
-    w >>= FIXED_BITS;
-    h >>= FIXED_BITS;
-
-    // set up all texture units
-    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
-        if (!c->rasterizer.state.texture[i].enable)
-            continue;
-
-        int32_t texcoords[8];
-        texture_unit_t& u(c->textures.tmu[i]);
-
-        // validate this tmu (bind, wrap, filter)
-        validate_tmu(c, i);
-        // we CLAMP here, which works with premultiplied (s,t)
-        c->rasterizer.procs.texParameteri(c,
-                GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
-        c->rasterizer.procs.texParameteri(c,
-                GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
-        u.dirty = 0xFF; // XXX: should be more subtle
-
-        EGLTextureObject* textureObject = u.texture;
-        const GLint Ucr = textureObject->crop_rect[0] << 16;
-        const GLint Vcr = textureObject->crop_rect[1] << 16;
-        const GLint Wcr = textureObject->crop_rect[2] << 16;
-        const GLint Hcr = textureObject->crop_rect[3] << 16;
-
-        // computes texture coordinates (pre-multiplied)
-        int32_t dsdx = Wcr / w;   // dsdx =  ((Wcr/w)/Wt)*Wt
-        int32_t dtdy =-Hcr / h;   // dtdy = -((Hcr/h)/Ht)*Ht
-        int32_t s0   = Ucr       - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx
-        int32_t t0   = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy
-        texcoords[0] = s0;
-        texcoords[1] = dsdx;
-        texcoords[2] = 0;
-        texcoords[3] = t0;
-        texcoords[4] = 0;
-        texcoords[5] = dtdy;
-        texcoords[6] = 0;
-        texcoords[7] = 0;
-        c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords);
-    }
-
-    const uint32_t enables = c->rasterizer.state.enables;
-    if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
-        set_depth_and_fog(c, z);
-
-    c->rasterizer.procs.activeTexture(c, c->textures.active);
-    c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
-    c->rasterizer.procs.disable(c, GGL_W_LERP);
-    c->rasterizer.procs.disable(c, GGL_AA);
-    c->rasterizer.procs.shadeModel(c, GL_FLAT);
-    c->rasterizer.procs.recti(c,
-            gglFixedToIntRound(x),
-            gglFixedToIntRound(y),
-            gglFixedToIntRound(x)+w,
-            gglFixedToIntRound(y)+h);
-
-    ogles_unlock_textures(c);
-}
-
-static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
-        ogles_context_t* c)
-{
-    // quickly reject empty rects
-    if ((w|h) <= 0)
-        return;
-
-    drawTexxOESImp(x, y, z, w, h, c);
-}
-
-static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c)
-{
-    // All coordinates are integer, so if we have only one
-    // texture unit active and no scaling is required
-    // THEN, we can use our special 1:1 mapping
-    // which is a lot faster.
-
-    if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) {
-        const int tmu = 0;
-        texture_unit_t& u(c->textures.tmu[tmu]);
-        EGLTextureObject* textureObject = u.texture;
-        const GLint Wcr = textureObject->crop_rect[2];
-        const GLint Hcr = textureObject->crop_rect[3];
-
-        if ((w == Wcr) && (h == -Hcr)) {
-            if ((w|h) <= 0) return; // quickly reject empty rects
-
-            if (u.dirty) {
-                c->rasterizer.procs.activeTexture(c, tmu);
-                c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
-                c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
-                        GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
-                c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
-                        GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
-            }
-            c->rasterizer.procs.texGeni(c, GGL_S,
-                    GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
-            c->rasterizer.procs.texGeni(c, GGL_T,
-                    GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
-            u.dirty = 0xFF; // XXX: should be more subtle
-            c->rasterizer.procs.activeTexture(c, c->textures.active);
-
-            const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
-            y = cbSurface.height - (y + h);
-            const GLint Ucr = textureObject->crop_rect[0];
-            const GLint Vcr = textureObject->crop_rect[1];
-            const GLint s0  = Ucr - x;
-            const GLint t0  = (Vcr + Hcr) - y;
-
-            const GLuint tw = textureObject->surface.width;
-            const GLuint th = textureObject->surface.height;
-            if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) {
-                // The GL spec is unclear about what should happen
-                // in this case, so we just use the slow case, which
-                // at least won't crash
-                goto slow_case;
-            }
-
-            ogles_lock_textures(c);
-
-            c->rasterizer.procs.texCoord2i(c, s0, t0);
-            const uint32_t enables = c->rasterizer.state.enables;
-            if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
-                set_depth_and_fog(c, gglIntToFixed(z));
-
-            c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
-            c->rasterizer.procs.disable(c, GGL_W_LERP);
-            c->rasterizer.procs.disable(c, GGL_AA);
-            c->rasterizer.procs.shadeModel(c, GL_FLAT);
-            c->rasterizer.procs.recti(c, x, y, x+w, y+h);
-
-            ogles_unlock_textures(c);
-
-            return;
-        }
-    }
-
-slow_case:
-    drawTexxOESImp(
-            gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z),
-            gglIntToFixed(w), gglIntToFixed(h),
-            c);
-}
-
-
-}; // namespace android
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-
-#if 0
-#pragma mark -
-#pragma mark Texture API
-#endif
-
-void glActiveTexture(GLenum texture)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    c->textures.active = texture - GL_TEXTURE0;
-    c->rasterizer.procs.activeTexture(c, c->textures.active);
-}
-
-void glBindTexture(GLenum target, GLuint texture)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    // Bind or create a texture
-    sp<EGLTextureObject> tex;
-    if (texture == 0) {
-        // 0 is our local texture object
-        tex = c->textures.defaultTexture;
-    } else {
-        tex = c->surfaceManager->texture(texture);
-        if (ggl_unlikely(tex == 0)) {
-            tex = c->surfaceManager->createTexture(texture);
-            if (tex == 0) {
-                ogles_error(c, GL_OUT_OF_MEMORY);
-                return;
-            }
-        }
-    }
-    bindTextureTmu(c, c->textures.active, texture, tex);
-}
-
-void glGenTextures(GLsizei n, GLuint *textures)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (n<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    // generate unique (shared) texture names
-    c->surfaceManager->getToken(n, textures);
-}
-
-void glDeleteTextures(GLsizei n, const GLuint *textures)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (n<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    // If deleting a bound texture, bind this unit to 0
-    for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) {
-        if (c->textures.tmu[t].name == 0)
-            continue;
-        for (int i=0 ; i<n ; i++) {
-            if (textures[i] && (textures[i] == c->textures.tmu[t].name)) {
-                // bind this tmu to texture 0
-                sp<EGLTextureObject> tex(c->textures.defaultTexture);
-                bindTextureTmu(c, t, 0, tex);
-            }
-        }
-    }
-    c->surfaceManager->deleteTextures(n, textures);
-    c->surfaceManager->recycleTokens(n, textures);
-}
-
-void glMultiTexCoord4f(
-        GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    const int tmu = target-GL_TEXTURE0;
-    c->current.texture[tmu].S = gglFloatToFixed(s);
-    c->current.texture[tmu].T = gglFloatToFixed(t);
-    c->current.texture[tmu].R = gglFloatToFixed(r);
-    c->current.texture[tmu].Q = gglFloatToFixed(q);
-}
-
-void glMultiTexCoord4x(
-        GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    const int tmu = target-GL_TEXTURE0;
-    c->current.texture[tmu].S = s;
-    c->current.texture[tmu].T = t;
-    c->current.texture[tmu].R = r;
-    c->current.texture[tmu].Q = q;
-}
-
-void glPixelStorei(GLenum pname, GLint param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if ((param<=0 || param>8) || (param & (param-1))) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (pname == GL_PACK_ALIGNMENT)
-        c->textures.packAlignment = param;
-    if (pname == GL_UNPACK_ALIGNMENT)
-        c->textures.unpackAlignment = param;
-}
-
-void glTexEnvf(GLenum target, GLenum pname, GLfloat param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.texEnvi(c, target, pname, GLint(param));
-}
-
-void glTexEnvfv(
-        GLenum target, GLenum pname, const GLfloat *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (pname == GL_TEXTURE_ENV_MODE) {
-        c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params));
-        return;
-    }
-    if (pname == GL_TEXTURE_ENV_COLOR) {
-        GGLfixed fixed[4];
-        for (int i=0 ; i<4 ; i++)
-            fixed[i] = gglFloatToFixed(params[i]);
-        c->rasterizer.procs.texEnvxv(c, target, pname, fixed);
-        return;
-    }
-    ogles_error(c, GL_INVALID_ENUM);
-}
-
-void glTexEnvx(GLenum target, GLenum pname, GLfixed param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.texEnvi(c, target, pname, param);
-}
-
-void glTexEnvxv(
-        GLenum target, GLenum pname, const GLfixed *params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->rasterizer.procs.texEnvxv(c, target, pname, params);
-}
-
-void glTexParameteriv(
-        GLenum target, GLenum pname, const GLint* params)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
-    switch (pname) {
-    case GL_TEXTURE_CROP_RECT_OES:
-        memcpy(textureObject->crop_rect, params, 4*sizeof(GLint));
-        break;
-    default:
-        texParameterx(target, pname, GLfixed(params[0]), c);
-        return;
-    }
-}
-
-void glTexParameterf(
-        GLenum target, GLenum pname, GLfloat param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    texParameterx(target, pname, GLfixed(param), c);
-}
-
-void glTexParameterx(
-        GLenum target, GLenum pname, GLfixed param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    texParameterx(target, pname, param, c);
-}
-
-void glTexParameteri(
-        GLenum target, GLenum pname, GLint param)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    texParameterx(target, pname, GLfixed(param), c);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#endif
-
-void glCompressedTexImage2D(
-        GLenum target, GLint level, GLenum internalformat,
-        GLsizei width, GLsizei height, GLint border,
-        GLsizei imageSize, const GLvoid *data)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (width<0 || height<0 || border!=0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    // "uncompress" the texture since pixelflinger doesn't support
-    // any compressed texture format natively.
-    GLenum format;
-    GLenum type;
-    switch (internalformat) {
-    case GL_PALETTE8_RGB8_OES:
-    case GL_PALETTE4_RGB8_OES:
-        format      = GL_RGB;
-        type        = GL_UNSIGNED_BYTE;
-        break;
-    case GL_PALETTE8_RGBA8_OES:
-    case GL_PALETTE4_RGBA8_OES:
-        format      = GL_RGBA;
-        type        = GL_UNSIGNED_BYTE;
-        break;
-    case GL_PALETTE8_R5_G6_B5_OES:
-    case GL_PALETTE4_R5_G6_B5_OES:
-        format      = GL_RGB;
-        type        = GL_UNSIGNED_SHORT_5_6_5;
-        break;
-    case GL_PALETTE8_RGBA4_OES:
-    case GL_PALETTE4_RGBA4_OES:
-        format      = GL_RGBA;
-        type        = GL_UNSIGNED_SHORT_4_4_4_4;
-        break;
-    case GL_PALETTE8_RGB5_A1_OES:
-    case GL_PALETTE4_RGB5_A1_OES:
-        format      = GL_RGBA;
-        type        = GL_UNSIGNED_SHORT_5_5_5_1;
-        break;
-#ifdef GL_OES_compressed_ETC1_RGB8_texture
-    case GL_ETC1_RGB8_OES:
-        format      = GL_RGB;
-        type        = GL_UNSIGNED_BYTE;
-        break;
-#endif
-    default:
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    if (!data || !width || !height) {
-        // unclear if this is an error or not...
-        return;
-    }
-
-    int32_t size;
-    GGLSurface* surface;
-
-#ifdef GL_OES_compressed_ETC1_RGB8_texture
-    if (internalformat == GL_ETC1_RGB8_OES) {
-        GLsizei compressedSize = etc1_get_encoded_data_size(width, height);
-        if (compressedSize > imageSize) {
-            ogles_error(c, GL_INVALID_VALUE);
-            return;
-        }
-        int error = createTextureSurface(c, &surface, &size,
-                level, format, type, width, height);
-        if (error) {
-            ogles_error(c, error);
-            return;
-        }
-        if (etc1_decode_image(
-                (const etc1_byte*)data,
-                (etc1_byte*)surface->data,
-                width, height, 3, surface->stride*3) != 0) {
-            ogles_error(c, GL_INVALID_OPERATION);
-        }
-        return;
-    }
-#endif
-
-    // all mipmap levels are specified at once.
-    const int numLevels = level<0 ? -level : 1;
-
-    if (dataSizePalette4(numLevels, width, height, format) > imageSize) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    for (int i=0 ; i<numLevels ; i++) {
-        int lod_w = (width  >> i) ? : 1;
-        int lod_h = (height >> i) ? : 1;
-        int error = createTextureSurface(c, &surface, &size,
-                i, format, type, lod_w, lod_h);
-        if (error) {
-            ogles_error(c, error);
-            return;
-        }
-        decodePalette4(data, i, width, height,
-                surface->data, surface->stride, internalformat);
-    }
-}
-
-
-void glTexImage2D(
-        GLenum target, GLint level, GLint internalformat,
-        GLsizei width, GLsizei height, GLint border,
-        GLenum format, GLenum type, const GLvoid *pixels)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (width<0 || height<0 || border!=0 || level < 0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (format != (GLenum)internalformat) {
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-    if (validFormatType(c, format, type)) {
-        return;
-    }
-
-    int32_t size = 0;
-    GGLSurface* surface = 0;
-    int error = createTextureSurface(c, &surface, &size,
-            level, format, type, width, height);
-    if (error) {
-        ogles_error(c, error);
-        return;
-    }
-
-    if (pixels) {
-        const int32_t formatIdx = convertGLPixelFormat(format, type);
-        const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
-        const int32_t align = c->textures.unpackAlignment-1;
-        const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
-        const int32_t stride = bpr / pixelFormat.size;
-
-        GGLSurface userSurface;
-        userSurface.version = sizeof(userSurface);
-        userSurface.width  = width;
-        userSurface.height = height;
-        userSurface.stride = stride;
-        userSurface.format = formatIdx;
-        userSurface.compressedFormat = 0;
-        userSurface.data = (GLubyte*)pixels;
-
-        int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height);
-        if (err) {
-            ogles_error(c, err);
-            return;
-        }
-        generateMipmap(c, level);
-    }
-}
-
-// ----------------------------------------------------------------------------
-
-void glCompressedTexSubImage2D(
-        GLenum /*target*/, GLint /*level*/, GLint /*xoffset*/,
-        GLint /*yoffset*/, GLsizei /*width*/, GLsizei /*height*/,
-        GLenum /*format*/, GLsizei /*imageSize*/,
-        const GLvoid* /*data*/)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    ogles_error(c, GL_INVALID_ENUM);
-}
-
-void glTexSubImage2D(
-        GLenum target, GLint level, GLint xoffset,
-        GLint yoffset, GLsizei width, GLsizei height,
-        GLenum format, GLenum type, const GLvoid *pixels)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (validFormatType(c, format, type)) {
-        return;
-    }
-
-    // find out which texture is bound to the current unit
-    const int active = c->textures.active;
-    EGLTextureObject* tex = c->textures.tmu[active].texture;
-    const GGLSurface& surface(tex->mip(level));
-
-    if (!tex->internalformat || tex->direct) {
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-
-    if (format != tex->internalformat) {
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-    if ((xoffset + width  > GLsizei(surface.width)) ||
-        (yoffset + height > GLsizei(surface.height))) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (!width || !height) {
-        return; // okay, but no-op.
-    }
-
-    // figure out the size we need as well as the stride
-    const int32_t formatIdx = convertGLPixelFormat(format, type);
-    if (formatIdx == 0) { // we don't know what to do with this
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-
-    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
-    const int32_t align = c->textures.unpackAlignment-1;
-    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
-    const int32_t stride = bpr / pixelFormat.size;
-    GGLSurface userSurface;
-    userSurface.version = sizeof(userSurface);
-    userSurface.width  = width;
-    userSurface.height = height;
-    userSurface.stride = stride;
-    userSurface.format = formatIdx;
-    userSurface.compressedFormat = 0;
-    userSurface.data = (GLubyte*)pixels;
-
-    int err = copyPixels(c,
-            surface, xoffset, yoffset,
-            userSurface, 0, 0, width, height);
-    if (err) {
-        ogles_error(c, err);
-        return;
-    }
-
-    generateMipmap(c, level);
-
-    // since we only changed the content of the texture, we don't need
-    // to call bindTexture on the main rasterizer.
-}
-
-// ----------------------------------------------------------------------------
-
-void glCopyTexImage2D(
-        GLenum target, GLint level, GLenum internalformat,
-        GLint x, GLint y, GLsizei width, GLsizei height,
-        GLint border)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (internalformat<GL_ALPHA || internalformat>GL_LUMINANCE_ALPHA) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (width<0 || height<0 || border!=0 || level<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    GLenum format = 0;
-    GLenum type = GL_UNSIGNED_BYTE;
-    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
-    const int cbFormatIdx = cbSurface.format;
-    switch (cbFormatIdx) {
-    case GGL_PIXEL_FORMAT_RGB_565:
-        type = GL_UNSIGNED_SHORT_5_6_5;
-        break;
-    case GGL_PIXEL_FORMAT_RGBA_5551:
-        type = GL_UNSIGNED_SHORT_5_5_5_1;
-        break;
-    case GGL_PIXEL_FORMAT_RGBA_4444:
-        type = GL_UNSIGNED_SHORT_4_4_4_4;
-        break;
-    }
-    switch (internalformat) {
-    case GL_ALPHA:
-    case GL_LUMINANCE_ALPHA:
-    case GL_LUMINANCE:
-        type = GL_UNSIGNED_BYTE;
-        break;
-    }
-
-    // figure out the format to use for the new texture
-    switch (cbFormatIdx) {
-    case GGL_PIXEL_FORMAT_RGBA_8888:
-    case GGL_PIXEL_FORMAT_A_8:
-    case GGL_PIXEL_FORMAT_RGBA_5551:
-    case GGL_PIXEL_FORMAT_RGBA_4444:
-        format = internalformat;
-        break;
-    case GGL_PIXEL_FORMAT_RGBX_8888:
-    case GGL_PIXEL_FORMAT_RGB_888:
-    case GGL_PIXEL_FORMAT_RGB_565:
-    case GGL_PIXEL_FORMAT_L_8:
-        switch (internalformat) {
-        case GL_LUMINANCE:
-        case GL_RGB:
-            format = internalformat;
-            break;
-        }
-        break;
-    }
-
-    if (format == 0) {
-        // invalid combination
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    // create the new texture...
-    int32_t size;
-    GGLSurface* surface;
-    int error = createTextureSurface(c, &surface, &size,
-            level, format, type, width, height);
-    if (error) {
-        ogles_error(c, error);
-        return;
-    }
-
-    // The bottom row is stored first in textures
-    GGLSurface txSurface(*surface);
-    txSurface.stride = -txSurface.stride;
-
-    // (x,y) is the lower-left corner of colorBuffer
-    y = cbSurface.height - (y + height);
-
-    /* The GLES spec says:
-     * If any of the pixels within the specified rectangle are outside
-     * the framebuffer associated with the current rendering context,
-     * then the values obtained for those pixels are undefined.
-     */
-    if (x+width > GLint(cbSurface.width))
-        width = cbSurface.width - x;
-
-    if (y+height > GLint(cbSurface.height))
-        height = cbSurface.height - y;
-
-    int err = copyPixels(c,
-            txSurface, 0, 0,
-            cbSurface, x, y, width, height);
-    if (err) {
-        ogles_error(c, err);
-    }
-
-    generateMipmap(c, level);
-}
-
-void glCopyTexSubImage2D(
-        GLenum target, GLint level, GLint xoffset, GLint yoffset,
-        GLint x, GLint y, GLsizei width, GLsizei height)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (!width || !height) {
-        return; // okay, but no-op.
-    }
-
-    // find out which texture is bound to the current unit
-    const int active = c->textures.active;
-    EGLTextureObject* tex = c->textures.tmu[active].texture;
-    const GGLSurface& surface(tex->mip(level));
-
-    if (!tex->internalformat) {
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-    if ((xoffset + width  > GLsizei(surface.width)) ||
-        (yoffset + height > GLsizei(surface.height))) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    // The bottom row is stored first in textures
-    GGLSurface txSurface(surface);
-    txSurface.stride = -txSurface.stride;
-
-    // (x,y) is the lower-left corner of colorBuffer
-    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
-    y = cbSurface.height - (y + height);
-
-    /* The GLES spec says:
-     * If any of the pixels within the specified rectangle are outside
-     * the framebuffer associated with the current rendering context,
-     * then the values obtained for those pixels are undefined.
-     */
-    if (x+width > GLint(cbSurface.width))
-        width = cbSurface.width - x;
-
-    if (y+height > GLint(cbSurface.height))
-        height = cbSurface.height - y;
-
-    int err = copyPixels(c,
-            txSurface, xoffset, yoffset,
-            cbSurface, x, y, width, height);
-    if (err) {
-        ogles_error(c, err);
-        return;
-    }
-
-    generateMipmap(c, level);
-}
-
-void glReadPixels(
-        GLint x, GLint y, GLsizei width, GLsizei height,
-        GLenum format, GLenum type, GLvoid *pixels)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if ((format != GL_RGBA) && (format != GL_RGB)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-    if (width<0 || height<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (x<0 || y<0) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    int32_t formatIdx = GGL_PIXEL_FORMAT_NONE;
-    if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) {
-        formatIdx = GGL_PIXEL_FORMAT_RGBA_8888;
-    } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) {
-        formatIdx = GGL_PIXEL_FORMAT_RGB_565;
-    } else {
-        ogles_error(c, GL_INVALID_OPERATION);
-        return;
-    }
-
-    const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s;
-    if ((x+width > GLint(readSurface.width)) ||
-            (y+height > GLint(readSurface.height))) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
-    const int32_t align = c->textures.packAlignment-1;
-    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
-    const int32_t stride = bpr / pixelFormat.size;
-
-    GGLSurface userSurface;
-    userSurface.version = sizeof(userSurface);
-    userSurface.width  = width;
-    userSurface.height = height;
-    userSurface.stride = -stride; // bottom row is transfered first
-    userSurface.format = formatIdx;
-    userSurface.compressedFormat = 0;
-    userSurface.data = (GLubyte*)pixels;
-
-    // use pixel-flinger to handle all the conversions
-    GGLContext* ggl = getRasterizer(c);
-    if (!ggl) {
-        // the only reason this would fail is because we ran out of memory
-        ogles_error(c, GL_OUT_OF_MEMORY);
-        return;
-    }
-
-    ggl->colorBuffer(ggl, &userSurface);  // destination is user buffer
-    ggl->bindTexture(ggl, &readSurface);  // source is read-buffer
-    ggl->texCoord2i(ggl, x, readSurface.height - (y + height));
-    ggl->recti(ggl, 0, 0, width, height);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark DrawTexture Extension
-#endif
-
-void glDrawTexsvOES(const GLshort* coords) {
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
-}
-void glDrawTexivOES(const GLint* coords) {
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
-}
-void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) {
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexiOES(x, y, z, w, h, c);
-}
-void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) {
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexiOES(x, y, z, w, h, c);
-}
-
-void glDrawTexfvOES(const GLfloat* coords) {
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexxOES(
-            gglFloatToFixed(coords[0]),
-            gglFloatToFixed(coords[1]),
-            gglFloatToFixed(coords[2]),
-            gglFloatToFixed(coords[3]),
-            gglFloatToFixed(coords[4]),
-            c);
-}
-void glDrawTexxvOES(const GLfixed* coords) {
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
-}
-void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexxOES(
-            gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z),
-            gglFloatToFixed(w), gglFloatToFixed(h),
-            c);
-}
-void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) {
-    ogles_context_t* c = ogles_context_t::get();
-    drawTexxOES(x, y, z, w, h, c);
-}
-
-// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark EGL Image Extension
-#endif
-
-void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    if (image == EGL_NO_IMAGE_KHR) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image;
-    if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    // bind it to the texture unit
-    sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
-    tex->setImage(native_buffer);
-}
-
-void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    if (target != GL_RENDERBUFFER_OES) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    if (image == EGL_NO_IMAGE_KHR) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image;
-    if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-    if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) {
-        ogles_error(c, GL_INVALID_VALUE);
-        return;
-    }
-
-    // well, we're not supporting this extension anyways
-}
diff --git a/opengl/libagl/texture.h b/opengl/libagl/texture.h
deleted file mode 100644
index 98f7550..0000000
--- a/opengl/libagl/texture.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* libs/opengles/texture.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_TEXTURE_H
-#define ANDROID_OPENGLES_TEXTURE_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-#include <private/pixelflinger/ggl_context.h>
-
-#include <GLES/gl.h>
-
-#include "context.h"
-
-namespace android {
-
-void ogles_init_texture(ogles_context_t* c);
-void ogles_uninit_texture(ogles_context_t* c);
-void ogles_validate_texture(ogles_context_t* c);
-void ogles_lock_textures(ogles_context_t* c);
-void ogles_unlock_textures(ogles_context_t* c);
-
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_TEXTURE_H
diff --git a/opengl/libagl/vertex.cpp b/opengl/libagl/vertex.cpp
deleted file mode 100644
index 9aacdb3..0000000
--- a/opengl/libagl/vertex.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-/* libs/opengles/vertex.cpp
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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 <stdio.h>
-#include <stdlib.h>
-#include "context.h"
-#include "fp.h"
-#include "vertex.h"
-#include "state.h"
-#include "matrix.h"
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-void ogles_init_vertex(ogles_context_t* c)
-{
-    c->cull.enable = GL_FALSE;
-    c->cull.cullFace = GL_BACK;
-    c->cull.frontFace = GL_CCW;
-
-    c->current.color.r = 0x10000;
-    c->current.color.g = 0x10000;
-    c->current.color.b = 0x10000;
-    c->current.color.a = 0x10000;
-
-    c->currentNormal.z = 0x10000;
-}
-
-void ogles_uninit_vertex(ogles_context_t* /*c*/)
-{
-}
-
-// ----------------------------------------------------------------------------
-// vertex processing
-// ----------------------------------------------------------------------------
-
-// Divides a vertex clip coordinates by W
-static inline
-void perspective(ogles_context_t* c, vertex_t* v, uint32_t enables)
-{
-    // [x,y,z]window = vpt * ([x,y,z]clip / clip.w)
-    // [w]window = 1/w
-
-    // With a regular projection generated by glFrustum(),
-    // we have w=-z, therefore, w is in [zNear, zFar].
-    // Also, zNear and zFar are stricly positive,
-    // and 1/w (window.w) is in [1/zFar, 1/zNear], usually this
-    // means ]0, +inf[ -- however, it is always recommended
-    // to use as large values as possible for zNear.
-    // All in all, w is usually smaller than 1.0 (assuming
-    // zNear is at least 1.0); and even if zNear is smaller than 1.0
-    // values of w won't be too big.
-
-    const int32_t rw = gglRecip28(v->clip.w);
-    const GLfixed* const m = c->transforms.vpt.transform.matrix.m;
-    v->window.w = rw;
-    v->window.x = gglMulAddx(gglMulx(v->clip.x, rw, 16), m[ 0], m[12], 28); 
-    v->window.y = gglMulAddx(gglMulx(v->clip.y, rw, 16), m[ 5], m[13], 28);
-    v->window.x = TRI_FROM_FIXED(v->window.x);
-    v->window.y = TRI_FROM_FIXED(v->window.y);
-    if (enables & GGL_ENABLE_DEPTH_TEST) {
-        v->window.z = gglMulAddx(gglMulx(v->clip.z, rw, 16), m[10], m[14], 28);
-    }
-}
-
-// frustum clipping and W-divide
-static inline
-void clipFrustumPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables)
-{
-    // ndc = clip / W
-    // window = ncd * viewport
-    
-    // clip to the view-volume
-    uint32_t clip = v->flags & vertex_t::CLIP_ALL;
-    const GLfixed w = v->clip.w;
-    if (v->clip.x < -w)   clip |= vertex_t::CLIP_L;
-    if (v->clip.x >  w)   clip |= vertex_t::CLIP_R;
-    if (v->clip.y < -w)   clip |= vertex_t::CLIP_B;
-    if (v->clip.y >  w)   clip |= vertex_t::CLIP_T;
-    if (v->clip.z < -w)   clip |= vertex_t::CLIP_N;
-    if (v->clip.z >  w)   clip |= vertex_t::CLIP_F;
-
-    v->flags |= clip;
-    c->arrays.cull &= clip;
-
-    if (ggl_likely(!clip)) {
-        // if the vertex is clipped, we don't do the perspective
-        // divide, since we don't need its window coordinates.
-        perspective(c, v, enables);
-    }
-}
-
-// frustum clipping, user clipping and W-divide
-static inline
-void clipAllPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables)
-{
-    // compute eye coordinates
-    c->arrays.mv_transform(
-            &c->transforms.modelview.transform, &v->eye, &v->obj);
-    v->flags |= vertex_t::EYE;
-
-    // clip this vertex against each user clip plane
-    uint32_t clip = 0;
-    int planes = c->clipPlanes.enable;
-    while (planes) {
-        const int i = 31 - gglClz(planes);
-        planes &= ~(1<<i);
-        // XXX: we should have a special dot() for 2,3,4 coords vertices
-        GLfixed d = dot4(c->clipPlanes.plane[i].equation.v, v->eye.v);
-        if (d < 0) {
-            clip |= 0x100<<i;
-        }
-    }
-    v->flags |= clip;
-
-    clipFrustumPerspective(c, v, enables);
-}
-
-// ----------------------------------------------------------------------------
-
-void ogles_vertex_project(ogles_context_t* c, vertex_t* v) {
-    perspective(c, v, c->rasterizer.state.enables);
-}
-
-void ogles_vertex_perspective2D(ogles_context_t* c, vertex_t* v)
-{
-    // here we assume w=1.0 and the viewport transformation
-    // has been applied already.
-    c->arrays.cull = 0;
-    v->window.x = TRI_FROM_FIXED(v->clip.x);
-    v->window.y = TRI_FROM_FIXED(v->clip.y);
-    v->window.z = v->clip.z;
-    v->window.w = v->clip.w << 12;
-}
-
-void ogles_vertex_perspective3DZ(ogles_context_t* c, vertex_t* v) {
-    clipFrustumPerspective(c, v, GGL_ENABLE_DEPTH_TEST);
-}
-void ogles_vertex_perspective3D(ogles_context_t* c, vertex_t* v) {
-    clipFrustumPerspective(c, v, 0);
-}
-void ogles_vertex_clipAllPerspective3DZ(ogles_context_t* c, vertex_t* v) {
-    clipAllPerspective(c, v, GGL_ENABLE_DEPTH_TEST);
-}
-void ogles_vertex_clipAllPerspective3D(ogles_context_t* c, vertex_t* v) {
-    clipAllPerspective(c, v, 0);
-}
-
-static void clipPlanex(GLenum plane, const GLfixed* equ, ogles_context_t* c)
-{
-    const int p = plane - GL_CLIP_PLANE0;
-    if (ggl_unlikely(uint32_t(p) > (GL_CLIP_PLANE5 - GL_CLIP_PLANE0))) {
-        ogles_error(c, GL_INVALID_ENUM);
-        return;
-    }
-
-    vec4_t& equation = c->clipPlanes.plane[p].equation;
-    memcpy(equation.v, equ, sizeof(vec4_t));
-
-    ogles_validate_transform(c, transform_state_t::MVIT);
-    transform_t& mvit = c->transforms.mvit4;
-    mvit.point4(&mvit, &equation, &equation);
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-
-void glColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->current.color.r       = gglFloatToFixed(r);
-    c->currentColorClamped.r = gglClampx(c->current.color.r);
-    c->current.color.g       = gglFloatToFixed(g);
-    c->currentColorClamped.g = gglClampx(c->current.color.g);
-    c->current.color.b       = gglFloatToFixed(b);
-    c->currentColorClamped.b = gglClampx(c->current.color.b);
-    c->current.color.a       = gglFloatToFixed(a);
-    c->currentColorClamped.a = gglClampx(c->current.color.a);
-}
-
-void glColor4x(GLfixed r, GLfixed g, GLfixed b, GLfixed a)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->current.color.r = r;
-    c->current.color.g = g;
-    c->current.color.b = b;
-    c->current.color.a = a;
-    c->currentColorClamped.r = gglClampx(r);
-    c->currentColorClamped.g = gglClampx(g);
-    c->currentColorClamped.b = gglClampx(b);
-    c->currentColorClamped.a = gglClampx(a);
-}
-
-void glNormal3f(GLfloat x, GLfloat y, GLfloat z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->currentNormal.x = gglFloatToFixed(x);
-    c->currentNormal.y = gglFloatToFixed(y);
-    c->currentNormal.z = gglFloatToFixed(z);
-}
-
-void glNormal3x(GLfixed x, GLfixed y, GLfixed z)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    c->currentNormal.x = x;
-    c->currentNormal.y = y;
-    c->currentNormal.z = z;
-}
-
-// ----------------------------------------------------------------------------
-
-void glClipPlanef(GLenum plane, const GLfloat* equ)
-{
-    const GLfixed equx[4] = {
-            gglFloatToFixed(equ[0]),
-            gglFloatToFixed(equ[1]),
-            gglFloatToFixed(equ[2]),
-            gglFloatToFixed(equ[3])
-    };
-    ogles_context_t* c = ogles_context_t::get();
-    clipPlanex(plane, equx, c);
-}
-
-void glClipPlanex(GLenum plane, const GLfixed* equ)
-{
-    ogles_context_t* c = ogles_context_t::get();
-    clipPlanex(plane, equ, c);
-}
diff --git a/opengl/libagl/vertex.h b/opengl/libagl/vertex.h
deleted file mode 100644
index 55e6213..0000000
--- a/opengl/libagl/vertex.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* libs/opengles/vertex.h
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT 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_OPENGLES_VERTEX_H
-#define ANDROID_OPENGLES_VERTEX_H
-
-#include <stdint.h>
-#include <stddef.h>
-#include <sys/types.h>
-
-namespace android {
-
-namespace gl {
-struct vertex_t;
-struct ogles_context_t;
-};
-
-void ogles_init_vertex(ogles_context_t* c);
-void ogles_uninit_vertex(ogles_context_t* c);
-
-void ogles_vertex_perspective2D(ogles_context_t*, vertex_t*);
-
-void ogles_vertex_perspective3D(ogles_context_t*, vertex_t*);
-void ogles_vertex_perspective3DZ(ogles_context_t*, vertex_t*);
-void ogles_vertex_clipAllPerspective3D(ogles_context_t*, vertex_t*);
-void ogles_vertex_clipAllPerspective3DZ(ogles_context_t*, vertex_t*);
-
-
-void ogles_vertex_project(ogles_context_t* c, vertex_t*);
-
-}; // namespace android
-
-#endif // ANDROID_OPENGLES_VERTEX_H
-
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 3c741ab..e255b9d 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -159,7 +159,7 @@
         "libEGL_getProcAddress",
         "libEGL_blobCache",
     ],
-    ldflags: ["-Wl,--exclude-libs=ALL"],
+    ldflags: ["-Wl,--exclude-libs=ALL,--Bsymbolic-functions"],
     export_include_dirs: ["EGL/include"],
     stubs: {
         symbol_file: "libEGL.map.txt",
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 038a432..e19802b 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -51,12 +51,6 @@
  *      /vendor/lib/egl/libGLESv1_CM.so
  *      /vendor/lib/egl/libGLESv2.so
  *
- * The software renderer for the emulator must be provided as a single
- * library at:
- *
- *      /system/lib/egl/libGLES_android.so
- *
- *
  * For backward compatibility and to facilitate the transition to
  * this new naming scheme, the loader will additionally look for:
  *
@@ -146,38 +140,6 @@
 #endif
 #endif
 
-static void setEmulatorGlesValue(void) {
-    char prop[PROPERTY_VALUE_MAX];
-    property_get("ro.kernel.qemu", prop, "0");
-    if (atoi(prop) != 1) return;
-
-    property_get("ro.kernel.qemu.gles",prop,"0");
-    if (atoi(prop) == 1) {
-        ALOGD("Emulator has host GPU support, qemu.gles is set to 1.");
-        property_set("qemu.gles", "1");
-        return;
-    }
-
-    // for now, checking the following
-    // directory is good enough for emulator system images
-    const char* vendor_lib_path =
-#if defined(__LP64__)
-        "/vendor/lib64/egl";
-#else
-        "/vendor/lib/egl";
-#endif
-
-    const bool has_vendor_lib = (access(vendor_lib_path, R_OK) == 0);
-    if (has_vendor_lib) {
-        ALOGD("Emulator has vendor provided software renderer, qemu.gles is set to 2.");
-        property_set("qemu.gles", "2");
-    } else {
-        ALOGD("Emulator without GPU support detected. "
-              "Fallback to legacy software renderer, qemu.gles is set to 0.");
-        property_set("qemu.gles", "0");
-    }
-}
-
 static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl";
 
 static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = {
@@ -260,8 +222,6 @@
         return cnx->dso;
     }
 
-    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;
@@ -278,6 +238,11 @@
 
     bool failToLoadFromDriverSuffixProperty = false;
     if (!hnd) {
+        // If updated driver apk is set but fail to load, abort here.
+        if (android::GraphicsEnv::getInstance().getDriverNamespace()) {
+            LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s",
+                             android::GraphicsEnv::getInstance().getDriverPath().c_str());
+        }
         // Finally, try to load system driver, start by searching for the library name appended by
         // the system properties of the GLES userspace driver in both locations.
         // i.e.:
@@ -311,7 +276,7 @@
     }
 
     if (!hnd) {
-        android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL,
+        android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL,
                                                             false, systemTime() - openTime);
     }
 
@@ -330,7 +295,7 @@
     }
 
     if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) {
-        android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL,
+        android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL,
                                                             false, systemTime() - openTime);
     }
 
@@ -338,9 +303,9 @@
             "couldn't load system EGL wrapper libraries");
 
     LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1,
-            "couldn't load system OpenGL ES wrapper libraries");
+                        "couldn't load system OpenGL ES wrapper libraries");
 
-    android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, true,
+    android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, true,
                                                         systemTime() - openTime);
 
     return (void*)hnd;
@@ -637,7 +602,7 @@
         return nullptr;
     }
 
-    android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::ANGLE);
+    android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE);
     driver_t* hnd = nullptr;
 
     // ANGLE doesn't ship with GLES library, and thus we skip GLES driver.
@@ -666,7 +631,7 @@
     }
 
     ALOGD("Load updated gl driver.");
-    android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL_UPDATED);
+    android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL_UPDATED);
     driver_t* hnd = nullptr;
     void* dso = load_updated_driver("GLES", ns);
     if (dso) {
@@ -697,7 +662,7 @@
 Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
                                                         const bool exact) {
     ATRACE_CALL();
-    android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL);
+    android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL);
     driver_t* hnd = nullptr;
     void* dso = load_system_driver("GLES", suffix, exact);
     if (dso) {
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 25b1009..0581708 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -172,10 +172,6 @@
 
 // ----------------------------------------------------------------------------
 
-// this mutex protects:
-//    d->disp[]
-//    egl_init_drivers_locked()
-//
 static EGLBoolean egl_init_drivers_locked() {
     if (sEarlyInitState) {
         // initialized by static ctor. should be set here.
@@ -202,6 +198,9 @@
     return cnx->dso ? EGL_TRUE : EGL_FALSE;
 }
 
+
+// this mutex protects driver load logic as a critical section since it accesses to global variable
+// like gEGLImpl
 static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
 
 EGLBoolean egl_init_drivers() {
diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp
index 408e76b..44a1c0b 100644
--- a/opengl/libs/EGL/egl_layers.cpp
+++ b/opengl/libs/EGL/egl_layers.cpp
@@ -151,7 +151,7 @@
 const char kSystemLayerLibraryDir[] = "/data/local/debug/gles";
 
 std::string LayerLoader::GetDebugLayers() {
-    // Layers can be specified at the Java level in GraphicsEnvironemnt
+    // Layers can be specified at the Java level in GraphicsEnvironment
     // gpu_debug_layers_gles = layer1:layer2:layerN
     std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES();
 
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index a3bb6de..c8840f9 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -976,22 +976,21 @@
                 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;
+            int version = egl_connection_t::GLESv1_INDEX;
             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;
-                        }
+                    if (attr == EGL_CONTEXT_CLIENT_VERSION && (value == 2 || value == 3)) {
+                        version = egl_connection_t::GLESv2_INDEX;
                     }
                 };
             }
-            egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
-                    version);
+            if (version == egl_connection_t::GLESv1_INDEX) {
+                android::GraphicsEnv::getInstance().setTargetStats(
+                        android::GpuStatsInfo::Stats::GLES_1_IN_USE);
+            }
+            egl_context_t* c = new egl_context_t(dpy, context, config, cnx, version);
             return c;
         }
     }
@@ -1398,6 +1397,9 @@
     if (!_s.get())
         return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
 
+    if (n_rects < 0 || (n_rects > 0 && rects == NULL))
+        return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE);
+
     egl_surface_t* const s = get_surface(draw);
 
     if (CC_UNLIKELY(dp->traceGpuCompletion)) {
diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp
index a0bd4e2..dfb9c92 100644
--- a/opengl/tests/lib/WindowSurface.cpp
+++ b/opengl/tests/lib/WindowSurface.cpp
@@ -16,10 +16,13 @@
 
 #include <WindowSurface.h>
 
-#include <gui/SurfaceComposerClient.h>
+#include <utility>
+
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
-#include <ui/DisplayInfo.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
 
 using namespace android;
 
@@ -33,29 +36,33 @@
         return;
     }
 
-    // Get main display parameters.
-    const auto mainDpy = SurfaceComposerClient::getInternalDisplayToken();
-    if (mainDpy == nullptr) {
+    const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+    if (displayToken == nullptr) {
         fprintf(stderr, "ERROR: no display\n");
         return;
     }
 
-    DisplayInfo mainDpyInfo;
-    err = SurfaceComposerClient::getDisplayInfo(mainDpy, &mainDpyInfo);
+    DisplayConfig displayConfig;
+    err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
     if (err != NO_ERROR) {
-        fprintf(stderr, "ERROR: unable to get display characteristics\n");
+        fprintf(stderr, "ERROR: unable to get active display config\n");
         return;
     }
 
-    uint32_t width, height;
-    if (mainDpyInfo.orientation != DISPLAY_ORIENTATION_0 &&
-            mainDpyInfo.orientation != DISPLAY_ORIENTATION_180) {
-        // rotated
-        width = mainDpyInfo.h;
-        height = mainDpyInfo.w;
-    } else {
-        width = mainDpyInfo.w;
-        height = mainDpyInfo.h;
+    ui::DisplayState displayState;
+    err = SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "ERROR: unable to get display state\n");
+        return;
+    }
+
+    const ui::Size& resolution = displayConfig.resolution;
+    auto width = resolution.getWidth();
+    auto height = resolution.getHeight();
+
+    if (displayState.orientation == ui::ROTATION_90 ||
+        displayState.orientation == ui::ROTATION_270) {
+        std::swap(width, height);
     }
 
     sp<SurfaceControl> sc = surfaceComposerClient->createSurface(
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
new file mode 100644
index 0000000..8ff0711
--- /dev/null
+++ b/services/automotive/display/Android.bp
@@ -0,0 +1,43 @@
+//
+// 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.
+//
+
+cc_binary {
+    name: "android.frameworks.automotive.display@1.0-service",
+    defaults: ["hidl_defaults"],
+    srcs: [
+        "main_automotivedisplayproxy.cpp",
+        "AutomotiveDisplayProxyService.cpp",
+    ],
+    init_rc: ["android.frameworks.automotive.display@1.0-service.rc"],
+
+    shared_libs: [
+        "android.frameworks.automotive.display@1.0",
+        "android.hardware.graphics.bufferqueue@2.0",
+        "libgui",
+        "libhidlbase",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    cflags: [
+        "-DLOG_TAG=\"AutomotiveDisplayService\""
+    ],
+}
diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp
new file mode 100644
index 0000000..4767406
--- /dev/null
+++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp
@@ -0,0 +1,193 @@
+//
+// 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 <utility>
+
+#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
+
+#include "AutomotiveDisplayProxyService.h"
+
+namespace android {
+namespace frameworks {
+namespace automotive {
+namespace display {
+namespace V1_0 {
+namespace implementation {
+
+
+Return<sp<IGraphicBufferProducer>>
+AutomotiveDisplayProxyService::getIGraphicBufferProducer(uint64_t id) {
+    auto it = mDisplays.find(id);
+    sp<IBinder> displayToken = nullptr;
+    sp<SurfaceControl> surfaceControl = nullptr;
+    if (it == mDisplays.end()) {
+        displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+        if (displayToken == nullptr) {
+            ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
+            return nullptr;
+        }
+
+        // Get the resolution from stored display state.
+        DisplayConfig displayConfig = {};
+        auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+        if (err != NO_ERROR) {
+            ALOGE("Failed to get display configuration of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+            return nullptr;
+        }
+
+        ui::DisplayState displayState = {};
+        err = SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+        if (err != NO_ERROR) {
+            ALOGE("Failed to get current display status of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+            return nullptr;
+        }
+
+        auto displayWidth  = displayConfig.resolution.getWidth();
+        auto displayHeight = displayConfig.resolution.getHeight();
+        if ((displayState.orientation != ui::ROTATION_0) &&
+            (displayState.orientation != ui::ROTATION_180)) {
+            std::swap(displayWidth, displayHeight);
+        }
+
+        sp<android::SurfaceComposerClient> surfaceClient = new SurfaceComposerClient();
+        err = surfaceClient->initCheck();
+        if (err != NO_ERROR) {
+            ALOGE("SurfaceComposerClient::initCheck error: %#x", err);
+            return nullptr;
+        }
+
+        // Create a SurfaceControl instance
+        surfaceControl = surfaceClient->createSurface(
+                String8::format("AutomotiveDisplay::%lX", (unsigned long)id),
+                displayWidth, displayHeight,
+                PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
+        if (surfaceControl == nullptr || !surfaceControl->isValid()) {
+            ALOGE("Failed to create SurfaceControl.");
+            return nullptr;
+        }
+
+        // Store
+        DisplayDesc descriptor = {displayToken, surfaceControl};
+        mDisplays.insert_or_assign(id, std::move(descriptor));
+    } else {
+        displayToken = it->second.token;
+        surfaceControl = it->second.surfaceControl;
+    }
+
+    // SurfaceControl::getSurface is guaranteed to be not null.
+    auto targetSurface = surfaceControl->getSurface();
+    return new ::android::hardware::graphics::bufferqueue::V2_0::utils::
+               B2HGraphicBufferProducer(targetSurface->getIGraphicBufferProducer());
+}
+
+
+Return<bool> AutomotiveDisplayProxyService::showWindow(uint64_t id) {
+    auto it = mDisplays.find(id);
+    if (it == mDisplays.end()) {
+        ALOGE("Given display token is invalid or unknown.");
+        return false;
+    }
+
+    ui::DisplayState displayState;
+    auto err = SurfaceComposerClient::getDisplayState(it->second.token, &displayState);
+    if (err != NO_ERROR) {
+        ALOGE("Failed to get current state of the display 0x%lX", (unsigned long)id);
+        return false;
+    }
+
+    SurfaceComposerClient::Transaction t;
+    t.setDisplayLayerStack(it->second.token, displayState.layerStack);
+    t.setLayerStack(it->second.surfaceControl, displayState.layerStack);
+
+    status_t status = t.setLayer(it->second.surfaceControl, 0x7FFFFFFF)
+                      .show(it->second.surfaceControl)
+                      .apply();
+
+    return status == NO_ERROR;
+}
+
+
+Return<bool> AutomotiveDisplayProxyService::hideWindow(uint64_t id) {
+    auto it = mDisplays.find(id);
+    if (it == mDisplays.end()) {
+        ALOGE("Given display token is invalid or unknown.");
+        return false;
+    }
+
+    status_t status = SurfaceComposerClient::Transaction{}
+                      .hide(it->second.surfaceControl)
+                      .apply();
+
+    return status == NO_ERROR;
+}
+
+
+Return<void> AutomotiveDisplayProxyService::getDisplayIdList(getDisplayIdList_cb _cb) {
+    hardware::hidl_vec<uint64_t> ids;
+
+    // Get stable IDs of all available displays and get their tokens and
+    // descriptors.
+    auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
+    ids.resize(displayIds.size());
+    for (auto i = 0; i < displayIds.size(); ++i) {
+        ids[i] = displayIds[i];
+    }
+
+    _cb(ids);
+    return hardware::Void();
+}
+
+
+Return<void> AutomotiveDisplayProxyService::getDisplayInfo(uint64_t id, getDisplayInfo_cb _cb) {
+    HwDisplayConfig activeConfig;
+    HwDisplayState  activeState;
+
+    auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id);
+    if (displayToken == nullptr) {
+        ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id);
+    } else {
+        DisplayConfig displayConfig = {};
+        auto err = SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+        if (err != NO_ERROR) {
+            ALOGW("Failed to get display configuration of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+        }
+
+        ui::DisplayState displayState = {};
+        err = SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+        if (err != NO_ERROR) {
+            ALOGW("Failed to get current display status of %lX.  "
+                  "This display will be ignored.", (unsigned long)id);
+        }
+
+        activeConfig.setToExternal((uint8_t*)&displayConfig, sizeof(DisplayConfig));
+        activeState.setToExternal((uint8_t*)&displayState, sizeof(DisplayState));
+    }
+
+    _cb(activeConfig, activeState);
+    return hardware::Void();
+}
+
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace display
+}  // namespace automotive
+}  // namespace frameworks
+}  // namespace android
+
diff --git a/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
new file mode 100644
index 0000000..ffb7b5e
--- /dev/null
+++ b/services/automotive/display/android.frameworks.automotive.display@1.0-service.rc
@@ -0,0 +1,5 @@
+service automotive_display /system/bin/android.frameworks.automotive.display@1.0-service
+    class hal
+    user graphics
+    group automotive_evs
+    disabled  # will not automatically start with its class; must be explicitly started.
diff --git a/services/automotive/display/include/AutomotiveDisplayProxyService.h b/services/automotive/display/include/AutomotiveDisplayProxyService.h
new file mode 100644
index 0000000..e2fc0d2
--- /dev/null
+++ b/services/automotive/display/include/AutomotiveDisplayProxyService.h
@@ -0,0 +1,66 @@
+//
+// 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.
+//
+#pragma once
+
+#include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <ui/DisplayState.h>
+#include <tuple>
+#include <vector>
+
+namespace android {
+namespace frameworks {
+namespace automotive {
+namespace display {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::Return;
+using ::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer;
+using ::android::sp;
+
+
+typedef struct DisplayDesc {
+    sp<IBinder>        token;
+    sp<SurfaceControl> surfaceControl;
+} DisplayDesc;
+
+
+class AutomotiveDisplayProxyService : public IAutomotiveDisplayProxyService {
+public:
+    Return<sp<IGraphicBufferProducer>> getIGraphicBufferProducer(uint64_t id) override;
+    Return<bool> showWindow(uint64_t id) override;
+    Return<bool> hideWindow(uint64_t id) override;
+    Return<void> getDisplayIdList(getDisplayIdList_cb _cb) override;
+    Return<void> getDisplayInfo(uint64_t, getDisplayInfo_cb _cb) override;
+
+private:
+    uint8_t getDisplayPort(const uint64_t id) { return (id & 0xF); }
+
+    std::unordered_map<uint64_t, DisplayDesc> mDisplays;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace display
+}  // namespace automotive
+}  // namespace frameworks
+}  // namespace android
+
diff --git a/services/automotive/display/main_automotivedisplayproxy.cpp b/services/automotive/display/main_automotivedisplayproxy.cpp
new file mode 100644
index 0000000..59b584c
--- /dev/null
+++ b/services/automotive/display/main_automotivedisplayproxy.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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 <unistd.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include "AutomotiveDisplayProxyService.h"
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// Generated HIDL files
+using android::frameworks::automotive::display::V1_0::IAutomotiveDisplayProxyService;
+
+// The namespace in which all our implementation code lives
+using namespace android::frameworks::automotive::display::V1_0::implementation;
+using namespace android;
+
+const static char kServiceName[] = "default";
+
+int main() {
+    ALOGI("Automotive Display Proxy Service is starting");
+
+    android::sp<IAutomotiveDisplayProxyService> service =
+        new AutomotiveDisplayProxyService();
+
+    configureRpcThreadpool(1, true /* callerWillJoin */);
+
+    // Register our service -- if somebody is already registered by our name,
+    // they will be killed (their thread pool will throw an exception).
+    status_t status = service->registerAsService(kServiceName);
+    if (status == OK) {
+        ALOGD("%s is ready.", kServiceName);
+        joinRpcThreadpool();
+    } else {
+        ALOGE("Could not register service %s (%d).", kServiceName, status);
+    }
+
+    // In normal operation, we don't expect the thread pool to exit
+    ALOGE("Automotive Window Service is shutting down");
+
+    return 1;
+}
+
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 7b8e0f8..6eed24a 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -1,20 +1,6 @@
-filegroup {
-    name: "gpuservice_sources",
-    srcs: [
-        "GpuService.cpp",
-        "gpustats/GpuStats.cpp"
-    ],
-}
-
-filegroup {
-    name: "gpuservice_binary_sources",
-    srcs: ["main_gpuservice.cpp"],
-}
-
 cc_defaults {
     name: "gpuservice_defaults",
     cflags: [
-        "-DLOG_TAG=\"GpuService\"",
         "-Wall",
         "-Werror",
         "-Wformat",
@@ -22,31 +8,38 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-    srcs: [
-        ":gpuservice_sources",
-    ],
-    include_dirs: [
-        "frameworks/native/vulkan/vkjson",
-        "frameworks/native/vulkan/include",
+}
+
+cc_defaults {
+    name: "libgpuservice_defaults",
+    defaults: ["gpuservice_defaults"],
+    cflags: [
+        "-DLOG_TAG=\"GpuService\"",
     ],
     shared_libs: [
         "libbase",
         "libbinder",
         "libcutils",
+        "libgfxstats",
         "libgraphicsenv",
         "liblog",
         "libutils",
-        "libvulkan",
+        "libvkjson",
     ],
     static_libs: [
         "libserviceutils",
-        "libvkjson",
+    ],
+    export_static_lib_headers: [
+        "libserviceutils",
+    ],
+    export_shared_lib_headers: [
+        "libgraphicsenv",
     ],
 }
 
 cc_defaults {
-    name: "gpuservice_production_defaults",
-    defaults: ["gpuservice_defaults"],
+    name: "libgpuservice_production_defaults",
+    defaults: ["libgpuservice_defaults"],
     cflags: [
         "-fvisibility=hidden",
         "-fwhole-program-vtables", // requires ThinLTO
@@ -56,8 +49,23 @@
     },
 }
 
+filegroup {
+    name: "libgpuservice_sources",
+    srcs: [
+        "GpuService.cpp",
+    ],
+}
+
+cc_library_shared {
+    name: "libgpuservice",
+    defaults: ["libgpuservice_production_defaults"],
+    srcs: [
+        ":libgpuservice_sources",
+    ],
+}
+
 cc_defaults {
-    name: "gpuservice_binary",
+    name: "libgpuservice_binary",
     defaults: ["gpuservice_defaults"],
     shared_libs: [
         "libbinder",
@@ -68,9 +76,17 @@
     ldflags: ["-Wl,--export-dynamic"],
 }
 
+filegroup {
+    name: "gpuservice_binary_sources",
+    srcs: ["main_gpuservice.cpp"],
+}
+
 cc_binary {
     name: "gpuservice",
-    defaults: ["gpuservice_binary"],
+    defaults: ["libgpuservice_binary"],
     init_rc: ["gpuservice.rc"],
     srcs: [":gpuservice_binary_sources"],
+    shared_libs: [
+        "libgpuservice",
+    ],
 }
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index c8253e0..7bb0e4a 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -24,14 +24,13 @@
 #include <binder/Parcel.h>
 #include <binder/PermissionCache.h>
 #include <cutils/properties.h>
+#include <gpustats/GpuStats.h>
 #include <private/android_filesystem_config.h>
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
 #include <vkjson.h>
 
-#include "gpustats/GpuStats.h"
-
 namespace android {
 
 using base::StringAppendF;
@@ -51,24 +50,15 @@
 void GpuService::setGpuStats(const std::string& driverPackageName,
                              const std::string& driverVersionName, uint64_t driverVersionCode,
                              int64_t driverBuildTime, const std::string& appPackageName,
-                             const int32_t vulkanVersion, GraphicsEnv::Driver driver,
+                             const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
                              bool isDriverLoaded, int64_t driverLoadingTime) {
-    mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime,
-                      appPackageName, vulkanVersion, driver, isDriverLoaded, driverLoadingTime);
-}
-
-status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const {
-    mGpuStats->pullGlobalStats(outStats);
-    return OK;
-}
-
-status_t GpuService::getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const {
-    mGpuStats->pullAppStats(outStats);
-    return OK;
+    mGpuStats->insertDriverStats(driverPackageName, driverVersionName, driverVersionCode,
+                                 driverBuildTime, appPackageName, vulkanVersion, driver,
+                                 isDriverLoaded, driverLoadingTime);
 }
 
 void GpuService::setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                                const GraphicsEnv::Stats stats, const uint64_t value) {
+                                const GpuStatsInfo::Stats stats, const uint64_t value) {
     mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value);
 }
 
@@ -99,22 +89,27 @@
         StringAppendF(&result, "Permission Denial: can't dump gpu from pid=%d, uid=%d\n", pid, uid);
     } else {
         bool dumpAll = true;
-        size_t index = 0;
+        bool dumpDriverInfo = false;
+        bool dumpStats = false;
         size_t numArgs = args.size();
 
         if (numArgs) {
-            if ((index < numArgs) && (args[index] == String16("--gpustats"))) {
-                index++;
-                mGpuStats->dump(args, &result);
-                dumpAll = false;
+            dumpAll = false;
+            for (size_t index = 0; index < numArgs; ++index) {
+                if (args[index] == String16("--gpustats")) {
+                    dumpStats = true;
+                } else if (args[index] == String16("--gpudriverinfo")) {
+                    dumpDriverInfo = true;
+                }
             }
         }
 
-        if (dumpAll) {
+        if (dumpAll || dumpDriverInfo) {
             dumpGameDriverInfo(&result);
             result.append("\n");
-
-            mGpuStats->dump(Vector<String16>(), &result);
+        }
+        if (dumpAll || dumpStats) {
+            mGpuStats->dump(args, &result);
             result.append("\n");
         }
     }
diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h
index 7d44a35..b3e34d5 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -46,12 +46,10 @@
     void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName,
                      uint64_t driverVersionCode, int64_t driverBuildTime,
                      const std::string& appPackageName, const int32_t vulkanVersion,
-                     GraphicsEnv::Driver driver, bool isDriverLoaded,
+                     GpuStatsInfo::Driver driver, bool isDriverLoaded,
                      int64_t driverLoadingTime) override;
-    status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const override;
-    status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const override;
     void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                        const GraphicsEnv::Stats stats, const uint64_t value) override;
+                        const GpuStatsInfo::Stats stats, const uint64_t value) override;
 
     /*
      * IBinder interface
diff --git a/services/gpuservice/TEST_MAPPING b/services/gpuservice/TEST_MAPPING
new file mode 100644
index 0000000..b345355
--- /dev/null
+++ b/services/gpuservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "gpuservice_unittest"
+    }
+  ]
+}
diff --git a/services/gpuservice/gpustats/Android.bp b/services/gpuservice/gpustats/Android.bp
new file mode 100644
index 0000000..f52602a
--- /dev/null
+++ b/services/gpuservice/gpustats/Android.bp
@@ -0,0 +1,29 @@
+cc_library_shared {
+    name: "libgfxstats",
+    srcs: [
+        "GpuStats.cpp",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libgraphicsenv",
+        "liblog",
+        "libprotoutil",
+        "libstatslog",
+        "libstatspull",
+        "libstatssocket",
+        "libutils",
+    ],
+    export_include_dirs: ["include"],
+    export_shared_lib_headers: [
+        "libstatspull",
+        "libstatssocket",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 5d27e72..d094532 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -17,30 +17,45 @@
 #define LOG_TAG "GpuStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
-#include "GpuStats.h"
+#include "gpustats/GpuStats.h"
 
+#include <android/util/ProtoOutputStream.h>
 #include <cutils/properties.h>
 #include <log/log.h>
+#include <stats_event.h>
+#include <statslog.h>
 #include <utils/Trace.h>
 
 #include <unordered_set>
 
 namespace android {
 
-static void addLoadingCount(GraphicsEnv::Driver driver, bool isDriverLoaded,
+GpuStats::GpuStats() {
+    AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO,
+                                           GpuStats::pullAtomCallback, nullptr, this);
+    AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_APP_INFO,
+                                           GpuStats::pullAtomCallback, nullptr, this);
+}
+
+GpuStats::~GpuStats() {
+    AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
+    AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+}
+
+static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
                             GpuStatsGlobalInfo* const outGlobalInfo) {
     switch (driver) {
-        case GraphicsEnv::Driver::GL:
-        case GraphicsEnv::Driver::GL_UPDATED:
+        case GpuStatsInfo::Driver::GL:
+        case GpuStatsInfo::Driver::GL_UPDATED:
             outGlobalInfo->glLoadingCount++;
             if (!isDriverLoaded) outGlobalInfo->glLoadingFailureCount++;
             break;
-        case GraphicsEnv::Driver::VULKAN:
-        case GraphicsEnv::Driver::VULKAN_UPDATED:
+        case GpuStatsInfo::Driver::VULKAN:
+        case GpuStatsInfo::Driver::VULKAN_UPDATED:
             outGlobalInfo->vkLoadingCount++;
             if (!isDriverLoaded) outGlobalInfo->vkLoadingFailureCount++;
             break;
-        case GraphicsEnv::Driver::ANGLE:
+        case GpuStatsInfo::Driver::ANGLE:
             outGlobalInfo->angleLoadingCount++;
             if (!isDriverLoaded) outGlobalInfo->angleLoadingFailureCount++;
             break;
@@ -49,22 +64,22 @@
     }
 }
 
-static void addLoadingTime(GraphicsEnv::Driver driver, int64_t driverLoadingTime,
+static void addLoadingTime(GpuStatsInfo::Driver driver, int64_t driverLoadingTime,
                            GpuStatsAppInfo* const outAppInfo) {
     switch (driver) {
-        case GraphicsEnv::Driver::GL:
-        case GraphicsEnv::Driver::GL_UPDATED:
+        case GpuStatsInfo::Driver::GL:
+        case GpuStatsInfo::Driver::GL_UPDATED:
             if (outAppInfo->glDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
                 outAppInfo->glDriverLoadingTime.emplace_back(driverLoadingTime);
             }
             break;
-        case GraphicsEnv::Driver::VULKAN:
-        case GraphicsEnv::Driver::VULKAN_UPDATED:
+        case GpuStatsInfo::Driver::VULKAN:
+        case GpuStatsInfo::Driver::VULKAN_UPDATED:
             if (outAppInfo->vkDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
                 outAppInfo->vkDriverLoadingTime.emplace_back(driverLoadingTime);
             }
             break;
-        case GraphicsEnv::Driver::ANGLE:
+        case GpuStatsInfo::Driver::ANGLE:
             if (outAppInfo->angleDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
                 outAppInfo->angleDriverLoadingTime.emplace_back(driverLoadingTime);
             }
@@ -74,10 +89,11 @@
     }
 }
 
-void GpuStats::insert(const std::string& driverPackageName, const std::string& driverVersionName,
-                      uint64_t driverVersionCode, int64_t driverBuildTime,
-                      const std::string& appPackageName, const int32_t vulkanVersion,
-                      GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) {
+void GpuStats::insertDriverStats(const std::string& driverPackageName,
+                                 const std::string& driverVersionName, uint64_t driverVersionCode,
+                                 int64_t driverBuildTime, const std::string& appPackageName,
+                                 const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+                                 bool isDriverLoaded, int64_t driverLoadingTime) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mLock);
@@ -127,7 +143,7 @@
 }
 
 void GpuStats::insertTargetStats(const std::string& appPackageName,
-                                 const uint64_t driverVersionCode, const GraphicsEnv::Stats stats,
+                                 const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
                                  const uint64_t /*value*/) {
     ATRACE_CALL();
 
@@ -139,9 +155,15 @@
     }
 
     switch (stats) {
-        case GraphicsEnv::Stats::CPU_VULKAN_IN_USE:
+        case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE:
             mAppStats[appStatsKey].cpuVulkanInUse = true;
             break;
+        case GpuStatsInfo::Stats::FALSE_PREROTATION:
+            mAppStats[appStatsKey].falsePrerotation = true;
+            break;
+        case GpuStatsInfo::Stats::GLES_1_IN_USE:
+            mAppStats[appStatsKey].gles1InUse = true;
+            break;
         default:
             break;
     }
@@ -185,6 +207,11 @@
         dumpAll = false;
     }
 
+    if (dumpAll) {
+        dumpGlobalLocked(result);
+        dumpAppLocked(result);
+    }
+
     if (argsSet.count("--clear")) {
         bool clearAll = true;
 
@@ -202,13 +229,6 @@
             mGlobalStats.clear();
             mAppStats.clear();
         }
-
-        dumpAll = false;
-    }
-
-    if (dumpAll) {
-        dumpGlobalLocked(result);
-        dumpAppLocked(result);
     }
 }
 
@@ -228,34 +248,114 @@
     }
 }
 
-void GpuStats::pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats) {
-    ATRACE_CALL();
+static std::string protoOutputStreamToByteString(android::util::ProtoOutputStream& proto) {
+    if (!proto.size()) return "";
 
-    std::lock_guard<std::mutex> lock(mLock);
-    outStats->clear();
-    outStats->reserve(mGlobalStats.size());
-
-    interceptSystemDriverStatsLocked();
-
-    for (const auto& ele : mGlobalStats) {
-        outStats->emplace_back(ele.second);
+    std::string byteString;
+    sp<android::util::ProtoReader> reader = proto.data();
+    while (reader->readBuffer() != nullptr) {
+        const size_t toRead = reader->currentToRead();
+        byteString.append((char*)reader->readBuffer(), toRead);
+        reader->move(toRead);
     }
 
-    mGlobalStats.clear();
+    if (byteString.size() != proto.size()) return "";
+
+    return byteString;
 }
 
-void GpuStats::pullAppStats(std::vector<GpuStatsAppInfo>* outStats) {
+static std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) {
+    if (value.empty()) return "";
+
+    android::util::ProtoOutputStream proto;
+    for (const auto& ele : value) {
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            1 /* field id */,
+                    (long long)ele);
+    }
+
+    return protoOutputStreamToByteString(proto);
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullAppInfoAtom(AStatsEventList* data) {
     ATRACE_CALL();
 
     std::lock_guard<std::mutex> lock(mLock);
-    outStats->clear();
-    outStats->reserve(mAppStats.size());
 
-    for (const auto& ele : mAppStats) {
-        outStats->emplace_back(ele.second);
+    if (data) {
+        for (const auto& ele : mAppStats) {
+            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+            AStatsEvent_setAtomId(event, android::util::GPU_STATS_APP_INFO);
+            AStatsEvent_writeString(event, ele.second.appPackageName.c_str());
+            AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+
+            std::string bytes = int64VectorToProtoByteString(ele.second.glDriverLoadingTime);
+            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+            bytes = int64VectorToProtoByteString(ele.second.vkDriverLoadingTime);
+            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+            bytes = int64VectorToProtoByteString(ele.second.angleDriverLoadingTime);
+            AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length());
+
+            AStatsEvent_writeBool(event, ele.second.cpuVulkanInUse);
+            AStatsEvent_writeBool(event, ele.second.falsePrerotation);
+            AStatsEvent_writeBool(event, ele.second.gles1InUse);
+            AStatsEvent_build(event);
+        }
     }
 
     mAppStats.clear();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullGlobalInfoAtom(AStatsEventList* data) {
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mLock);
+    // flush cpuVulkanVersion and glesVersion to builtin driver stats
+    interceptSystemDriverStatsLocked();
+
+    if (data) {
+        for (const auto& ele : mGlobalStats) {
+            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+            AStatsEvent_setAtomId(event, android::util::GPU_STATS_GLOBAL_INFO);
+            AStatsEvent_writeString(event, ele.second.driverPackageName.c_str());
+            AStatsEvent_writeString(event, ele.second.driverVersionName.c_str());
+            AStatsEvent_writeInt64(event, ele.second.driverVersionCode);
+            AStatsEvent_writeInt64(event, ele.second.driverBuildTime);
+            AStatsEvent_writeInt64(event, ele.second.glLoadingCount);
+            AStatsEvent_writeInt64(event, ele.second.glLoadingFailureCount);
+            AStatsEvent_writeInt64(event, ele.second.vkLoadingCount);
+            AStatsEvent_writeInt64(event, ele.second.vkLoadingFailureCount);
+            AStatsEvent_writeInt32(event, ele.second.vulkanVersion);
+            AStatsEvent_writeInt32(event, ele.second.cpuVulkanVersion);
+            AStatsEvent_writeInt32(event, ele.second.glesVersion);
+            AStatsEvent_writeInt64(event, ele.second.angleLoadingCount);
+            AStatsEvent_writeInt64(event, ele.second.angleLoadingFailureCount);
+            AStatsEvent_build(event);
+        }
+    }
+
+    mGlobalStats.clear();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn GpuStats::pullAtomCallback(int32_t atomTag,
+                                                                AStatsEventList* data,
+                                                                void* cookie) {
+    ATRACE_CALL();
+
+    GpuStats* pGpuStats = reinterpret_cast<GpuStats*>(cookie);
+    if (atomTag == android::util::GPU_STATS_GLOBAL_INFO) {
+        return pGpuStats->pullGlobalInfoAtom(data);
+    } else if (atomTag == android::util::GPU_STATS_APP_INFO) {
+        return pGpuStats->pullAppInfoAtom(data);
+    }
+
+    return AStatsManager_PULL_SKIP;
 }
 
 } // namespace android
diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
similarity index 62%
rename from services/gpuservice/gpustats/GpuStats.h
rename to services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 378f7f4..8ca4e4e 100644
--- a/services/gpuservice/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -16,41 +16,50 @@
 
 #pragma once
 
+#include <graphicsenv/GpuStatsInfo.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <stats_pull_atom_callback.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
 #include <mutex>
 #include <unordered_map>
 #include <vector>
 
-#include <graphicsenv/GpuStatsInfo.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
 namespace android {
 
 class GpuStats {
 public:
-    GpuStats() = default;
-    ~GpuStats() = default;
+    GpuStats();
+    ~GpuStats();
 
-    // Insert new gpu stats into global stats and app stats.
-    void insert(const std::string& driverPackageName, const std::string& driverVersionName,
-                uint64_t driverVersionCode, int64_t driverBuildTime,
-                const std::string& appPackageName, const int32_t vulkanVersion,
-                GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime);
+    // Insert new gpu driver stats into global stats and app stats.
+    void insertDriverStats(const std::string& driverPackageName,
+                           const std::string& driverVersionName, uint64_t driverVersionCode,
+                           int64_t driverBuildTime, const std::string& appPackageName,
+                           const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
+                           bool isDriverLoaded, int64_t driverLoadingTime);
     // Insert target stats into app stats or potentially global stats as well.
     void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode,
-                           const GraphicsEnv::Stats stats, const uint64_t value);
+                           const GpuStatsInfo::Stats stats, const uint64_t value);
     // dumpsys interface
     void dump(const Vector<String16>& args, std::string* result);
-    // Pull gpu global stats
-    void pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats);
-    // Pull gpu app stats
-    void pullAppStats(std::vector<GpuStatsAppInfo>* outStats);
 
     // This limits the worst case number of loading times tracked.
     static const size_t MAX_NUM_LOADING_TIMES = 50;
 
 private:
+    // Friend class for testing.
+    friend class TestableGpuStats;
+
+    // Native atom puller callback registered in statsd.
+    static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
+                                                                 AStatsEventList* data,
+                                                                 void* cookie);
+    // Pull global into into global atom.
+    AStatsManager_PullAtomCallbackReturn pullGlobalInfoAtom(AStatsEventList* data);
+    // Pull app into into app atom.
+    AStatsManager_PullAtomCallbackReturn pullAppInfoAtom(AStatsEventList* data);
     // Dump global stats
     void dumpGlobalLocked(std::string* result);
     // Dump app stats
diff --git a/vulkan/tools/Android.bp b/services/gpuservice/tests/unittests/Android.bp
similarity index 60%
copy from vulkan/tools/Android.bp
copy to services/gpuservice/tests/unittests/Android.bp
index 91d64fe..538506d 100644
--- a/vulkan/tools/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2015 The Android Open Source Project
+// Copyright 2020 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,24 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_binary {
-    name: "vkinfo",
-
-    clang: true,
-    cflags: [
-        "-fvisibility=hidden",
-        "-fstrict-aliasing",
-
-        "-DLOG_TAG=\"vkinfo\"",
-
-        "-Wextra",
-        "-Werror",
+cc_test {
+    name: "gpuservice_unittest",
+    test_suites: ["device-tests"],
+    sanitize: {
+        address: true,
+    },
+    srcs: [
+        "GpuStatsTest.cpp",
     ],
-
-    srcs: ["vkinfo.cpp"],
-
     shared_libs: [
-        "libvulkan",
+        "libcutils",
+        "libgfxstats",
+        "libgraphicsenv",
         "liblog",
+        "libstatslog",
+        "libstatspull",
+        "libutils",
+    ],
+    static_libs: [
+        "libgmock",
     ],
 }
diff --git a/services/gpuservice/tests/unittests/AndroidTest.xml b/services/gpuservice/tests/unittests/AndroidTest.xml
new file mode 100644
index 0000000..66f51c7
--- /dev/null
+++ b/services/gpuservice/tests/unittests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for gpuservice_unittest">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="gpuservice_unittest->/data/local/tmp/gpuservice_unittest" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="gpuservice_unittest" />
+    </test>
+</configuration>
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
new file mode 100644
index 0000000..37ebeae
--- /dev/null
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "gpuservice_unittest"
+
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <gpustats/GpuStats.h>
+#include <gtest/gtest.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "TestableGpuStats.h"
+
+namespace android {
+namespace {
+
+using testing::HasSubstr;
+
+// clang-format off
+#define BUILTIN_DRIVER_PKG_NAME   "system"
+#define BUILTIN_DRIVER_VER_NAME   "0"
+#define BUILTIN_DRIVER_VER_CODE   0
+#define BUILTIN_DRIVER_BUILD_TIME 123
+#define UPDATED_DRIVER_PKG_NAME   "updated"
+#define UPDATED_DRIVER_VER_NAME   "1"
+#define UPDATED_DRIVER_VER_CODE   1
+#define UPDATED_DRIVER_BUILD_TIME 234
+#define VULKAN_VERSION            345
+#define APP_PKG_NAME_1            "testapp1"
+#define APP_PKG_NAME_2            "testapp2"
+#define DRIVER_LOADING_TIME_1     678
+#define DRIVER_LOADING_TIME_2     789
+#define DRIVER_LOADING_TIME_3     891
+
+enum InputCommand : int32_t {
+    DUMP_ALL               = 0,
+    DUMP_GLOBAL            = 1,
+    DUMP_APP               = 2,
+    DUMP_ALL_THEN_CLEAR    = 3,
+    DUMP_GLOBAL_THEN_CLEAR = 4,
+    DUMP_APP_THEN_CLEAR    = 5,
+};
+// clang-format on
+
+class GpuStatsTest : public testing::Test {
+public:
+    GpuStatsTest() {
+        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());
+    }
+
+    ~GpuStatsTest() {
+        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);
+
+    void SetUp() override {
+        mCpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0);
+        mGlesVersion = property_get_int32("ro.opengles.version", 0);
+    }
+
+    std::unique_ptr<GpuStats> mGpuStats = std::make_unique<GpuStats>();
+    int32_t mCpuVulkanVersion = 0;
+    int32_t mGlesVersion = 0;
+};
+
+std::string GpuStatsTest::inputCommand(InputCommand cmd) {
+    std::string result;
+    Vector<String16> args;
+
+    switch (cmd) {
+        case InputCommand::DUMP_ALL:
+            break;
+        case InputCommand::DUMP_GLOBAL:
+            args.push_back(String16("--global"));
+            break;
+        case InputCommand::DUMP_APP:
+            args.push_back(String16("--app"));
+            break;
+        case InputCommand::DUMP_ALL_THEN_CLEAR:
+            args.push_back(String16("--clear"));
+            break;
+        case InputCommand::DUMP_GLOBAL_THEN_CLEAR:
+            args.push_back(String16("--global"));
+            args.push_back(String16("--clear"));
+            break;
+        case InputCommand::DUMP_APP_THEN_CLEAR:
+            args.push_back(String16("--app"));
+            args.push_back(String16("--clear"));
+            break;
+    }
+
+    mGpuStats->dump(args, &result);
+    return result;
+}
+
+TEST_F(GpuStatsTest, statsEmptyByDefault) {
+    ASSERT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertBuiltinDriverStats) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    std::string expectedResult = "driverPackageName = " + std::string(BUILTIN_DRIVER_PKG_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionName = " + std::string(BUILTIN_DRIVER_VER_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverBuildTime = " + std::to_string(BUILTIN_DRIVER_BUILD_TIME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("glLoadingFailureCount = 0"));
+    expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_1);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(BUILTIN_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "glDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_1);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertUpdatedDriverStats) {
+    mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+                                 UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::VULKAN_UPDATED, false,
+                                 DRIVER_LOADING_TIME_2);
+
+    std::string expectedResult = "driverPackageName = " + std::string(UPDATED_DRIVER_PKG_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionName = " + std::string(UPDATED_DRIVER_VER_NAME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "driverBuildTime = " + std::to_string(UPDATED_DRIVER_BUILD_TIME);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("vkLoadingFailureCount = 1"));
+    expectedResult = "appPackageName = " + std::string(APP_PKG_NAME_2);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "driverVersionCode = " + std::to_string(UPDATED_DRIVER_VER_CODE);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+    expectedResult = "vkDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_2);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canInsertAngleDriverStats) {
+    mGpuStats->insertDriverStats(UPDATED_DRIVER_PKG_NAME, UPDATED_DRIVER_VER_NAME,
+                                 UPDATED_DRIVER_VER_CODE, UPDATED_DRIVER_BUILD_TIME, APP_PKG_NAME_2,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::ANGLE, true,
+                                 DRIVER_LOADING_TIME_3);
+
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingCount = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr("angleLoadingFailureCount = 0"));
+    std::string expectedResult = "angleDriverLoadingTime: " + std::to_string(DRIVER_LOADING_TIME_3);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canDump3dApiVersion) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    std::string expectedResult = "vulkanVersion = " + std::to_string(VULKAN_VERSION);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "cpuVulkanVersion = " + std::to_string(mCpuVulkanVersion);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+    expectedResult = "glesVersion = " + std::to_string(mGlesVersion);
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_GLOBAL), HasSubstr(expectedResult));
+}
+
+TEST_F(GpuStatsTest, canNotInsertTargetStatsBeforeProperSetup) {
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canInsertTargetStatsAfterProperSetup) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+    mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+                                 GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
+    EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+}
+
+TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_ALL_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_ALL).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpGlobalBeforeClearGlobal) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canDumpAppBeforeClearApp) {
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP_THEN_CLEAR).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+}
+
+TEST_F(GpuStatsTest, skipPullInvalidAtom) {
+    TestableGpuStats testableGpuStats(mGpuStats.get());
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+    EXPECT_TRUE(testableGpuStats.makePullAtomCallback(-1) == AStatsManager_PULL_SKIP);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canPullGlobalAtom) {
+    TestableGpuStats testableGpuStats(mGpuStats.get());
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+    EXPECT_TRUE(testableGpuStats.makePullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO) ==
+                AStatsManager_PULL_SUCCESS);
+
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+TEST_F(GpuStatsTest, canPullAppAtom) {
+    TestableGpuStats testableGpuStats(mGpuStats.get());
+    mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+                                 BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+                                 VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+                                 DRIVER_LOADING_TIME_1);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_APP).empty());
+
+    EXPECT_TRUE(testableGpuStats.makePullAtomCallback(android::util::GPU_STATS_APP_INFO) ==
+                AStatsManager_PULL_SUCCESS);
+
+    EXPECT_FALSE(inputCommand(InputCommand::DUMP_GLOBAL).empty());
+    EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
+}
+
+} // namespace
+} // namespace android
diff --git a/services/gpuservice/tests/unittests/TestableGpuStats.h b/services/gpuservice/tests/unittests/TestableGpuStats.h
new file mode 100644
index 0000000..4ea564c
--- /dev/null
+++ b/services/gpuservice/tests/unittests/TestableGpuStats.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <gpustats/GpuStats.h>
+#include <stdint.h>
+
+namespace android {
+
+class TestableGpuStats {
+public:
+    explicit TestableGpuStats(GpuStats *gpuStats) : mGpuStats(gpuStats) {}
+
+    AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atomTag) {
+        return mGpuStats->pullAtomCallback(atomTag, nullptr, mGpuStats);
+    }
+
+private:
+    GpuStats *mGpuStats;
+};
+
+} // namespace android
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 11578c3..5c80d55 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -40,10 +40,12 @@
         "libinputreporter",
         "libinputreader",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "libhidlbase",
         "libinput",
         "liblog",
+        "libstatslog",
         "libutils",
         "libui",
         "server_configurable_flags",
@@ -82,6 +84,7 @@
     srcs: [
         "InputListener.cpp",
         "InputReaderBase.cpp",
+        "InputThread.cpp",
     ],
 
     shared_libs: [
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index ef1a224..ae9a348 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -276,7 +276,7 @@
     bool eventAdded = mEvents.push(std::move(event));
     if (!eventAdded) {
         // If the queue is full, suspect the HAL is slow in processing the events.
-        ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime);
+        ALOGE("Could not add the event to the queue. Resetting");
         reset();
     }
 }
diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp
index f82c8ef..fc8c7c3 100644
--- a/services/inputflinger/InputClassifierConverter.cpp
+++ b/services/inputflinger/InputClassifierConverter.cpp
@@ -358,6 +358,7 @@
     event.displayId = args.displayId;
     event.downTime = args.downTime;
     event.eventTime = args.eventTime;
+    event.deviceTimestamp = 0;
     event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK);
     event.actionIndex = getActionIndex(args.action);
     event.actionButton = getActionButton(args.actionButton);
@@ -375,7 +376,6 @@
     event.pointerProperties = pointerProperties;
     event.pointerCoords = pointerCoords;
 
-    event.deviceTimestamp = args.deviceTimestamp;
     event.frames = convertVideoFrames(args.videoFrames);
 
     return event;
diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp
index 423b69c..de63977 100644
--- a/services/inputflinger/InputListener.cpp
+++ b/services/inputflinger/InputListener.cpp
@@ -21,6 +21,7 @@
 #include "InputListener.h"
 
 #include <android/log.h>
+#include <math.h>
 
 namespace android {
 
@@ -87,21 +88,32 @@
 
 // --- NotifyMotionArgs ---
 
-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, MotionClassification classification,
-        int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
-        const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-        float xPrecision, float yPrecision, nsecs_t downTime,
-        const std::vector<TouchVideoFrame>& videoFrames) :
-        NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source),
-        displayId(displayId), policyFlags(policyFlags),
-        action(action), actionButton(actionButton),
-        flags(flags), metaState(metaState), buttonState(buttonState),
-        classification(classification), edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp),
+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, MotionClassification classification,
+        int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
+        const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+        float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+        const std::vector<TouchVideoFrame>& videoFrames)
+      : NotifyArgs(sequenceNum, eventTime),
+        deviceId(deviceId),
+        source(source),
+        displayId(displayId),
+        policyFlags(policyFlags),
+        action(action),
+        actionButton(actionButton),
+        flags(flags),
+        metaState(metaState),
+        buttonState(buttonState),
+        classification(classification),
+        edgeFlags(edgeFlags),
         pointerCount(pointerCount),
-        xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime),
+        xPrecision(xPrecision),
+        yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
+        downTime(downTime),
         videoFrames(videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         this->pointerProperties[i].copyFrom(pointerProperties[i]);
@@ -109,14 +121,25 @@
     }
 }
 
-NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) :
-        NotifyArgs(other.sequenceNum, 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),
-        classification(other.classification), edgeFlags(other.edgeFlags),
-        deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount),
-        xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime),
+NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other)
+      : NotifyArgs(other.sequenceNum, 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),
+        classification(other.classification),
+        edgeFlags(other.edgeFlags),
+        pointerCount(other.pointerCount),
+        xPrecision(other.xPrecision),
+        yPrecision(other.yPrecision),
+        xCursorPosition(other.xCursorPosition),
+        yCursorPosition(other.yCursorPosition),
+        downTime(other.downTime),
         videoFrames(other.videoFrames) {
     for (uint32_t i = 0; i < pointerCount; i++) {
         pointerProperties[i].copyFrom(other.pointerProperties[i]);
@@ -124,28 +147,23 @@
     }
 }
 
+static inline bool isCursorPositionEqual(float lhs, float rhs) {
+    return (isnan(lhs) && isnan(rhs)) || lhs == rhs;
+}
+
 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
+    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 &&
+            pointerCount == rhs.pointerCount
             // PointerProperties and PointerCoords are compared separately below
-            && xPrecision == rhs.xPrecision
-            && yPrecision == rhs.yPrecision
-            && downTime == rhs.downTime
-            && videoFrames == rhs.videoFrames;
+            && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision &&
+            isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) &&
+            isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) &&
+            downTime == rhs.downTime && videoFrames == rhs.videoFrames;
     if (!equal) {
         return false;
     }
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 7d30672..c7c61cf 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -20,7 +20,6 @@
 
 #include "InputManager.h"
 #include "InputDispatcherFactory.h"
-#include "InputDispatcherThread.h"
 #include "InputReaderFactory.h"
 
 #include <binder/IPCThreadState.h>
@@ -38,30 +37,24 @@
     mDispatcher = createInputDispatcher(dispatcherPolicy);
     mClassifier = new InputClassifier(mDispatcher);
     mReader = createInputReader(readerPolicy, mClassifier);
-    initialize();
 }
 
 InputManager::~InputManager() {
     stop();
 }
 
-void InputManager::initialize() {
-    mReaderThread = new InputReaderThread(mReader);
-    mDispatcherThread = new InputDispatcherThread(mDispatcher);
-}
-
 status_t InputManager::start() {
-    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+    status_t result = mDispatcher->start();
     if (result) {
         ALOGE("Could not start InputDispatcher thread due to error %d.", result);
         return result;
     }
 
-    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+    result = mReader->start();
     if (result) {
-        ALOGE("Could not start InputReader thread due to error %d.", result);
+        ALOGE("Could not start InputReader due to error %d.", result);
 
-        mDispatcherThread->requestExit();
+        mDispatcher->stop();
         return result;
     }
 
@@ -69,17 +62,21 @@
 }
 
 status_t InputManager::stop() {
-    status_t result = mReaderThread->requestExitAndWait();
+    status_t status = OK;
+
+    status_t result = mReader->stop();
     if (result) {
-        ALOGW("Could not stop InputReader thread due to error %d.", result);
+        ALOGW("Could not stop InputReader due to error %d.", result);
+        status = result;
     }
 
-    result = mDispatcherThread->requestExitAndWait();
+    result = mDispatcher->stop();
     if (result) {
         ALOGW("Could not stop InputDispatcher thread due to error %d.", result);
+        status = result;
     }
 
-    return OK;
+    return status;
 }
 
 sp<InputReaderInterface> InputManager::getReader() {
@@ -128,7 +125,7 @@
                 "from non shell/root entity (PID: %d)", ipc->getCallingPid());
         return;
     }
-    mDispatcher->registerInputChannel(channel, false);
+    mDispatcher->registerInputChannel(channel);
 }
 
 void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) {
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 40f66d8..586097f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
 /*
  * The input manager is the core of the system event processing.
  *
- * The input manager uses two threads.
+ * The input manager has two components.
  *
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- *    applies policy, and posts messages to a queue managed by the DispatcherThread.
- * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
+ * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
+ *    policy, and posts messages to a queue managed by the InputDispatcherThread.
+ * 2. The InputDispatcher class starts a thread that waits for new events on the
  *    queue and asynchronously dispatches them to applications.
  *
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state.  Moreover, all communication is done one way from the InputReaderThread
+ * By design, the InputReader class and InputDispatcher class do not share any
+ * internal state.  Moreover, all communication is done one way from the InputReader
  * into the InputDispatcherThread and never the reverse.  Both classes may interact with the
  * InputDispatchPolicy, however.
  *
@@ -65,10 +65,10 @@
     virtual ~InputManagerInterface() { }
 
 public:
-    /* Starts the input manager threads. */
+    /* Starts the input threads. */
     virtual status_t start() = 0;
 
-    /* Stops the input manager threads and waits for them to exit. */
+    /* Stops the input threads and waits for them to exit. */
     virtual status_t stop() = 0;
 
     /* Gets the input reader. */
@@ -102,14 +102,10 @@
 
 private:
     sp<InputReaderInterface> mReader;
-    sp<InputReaderThread> mReaderThread;
 
     sp<InputClassifierInterface> mClassifier;
 
     sp<InputDispatcherInterface> mDispatcher;
-    sp<InputDispatcherThread> mDispatcherThread;
-
-    void initialize();
 };
 
 } // namespace android
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index f48a645..b2dadf8 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,22 +33,46 @@
 
 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::string InputReaderConfiguration::changesToString(uint32_t changes) {
+    if (changes == 0) {
+        return "<none>";
+    }
+    std::string result;
+    if (changes & CHANGE_POINTER_SPEED) {
+        result += "POINTER_SPEED | ";
+    }
+    if (changes & CHANGE_POINTER_GESTURE_ENABLEMENT) {
+        result += "POINTER_GESTURE_ENABLEMENT | ";
+    }
+    if (changes & CHANGE_DISPLAY_INFO) {
+        result += "DISPLAY_INFO | ";
+    }
+    if (changes & CHANGE_SHOW_TOUCHES) {
+        result += "SHOW_TOUCHES | ";
+    }
+    if (changes & CHANGE_KEYBOARD_LAYOUTS) {
+        result += "KEYBOARD_LAYOUTS | ";
+    }
+    if (changes & CHANGE_DEVICE_ALIAS) {
+        result += "DEVICE_ALIAS | ";
+    }
+    if (changes & CHANGE_TOUCH_AFFINE_TRANSFORMATION) {
+        result += "TOUCH_AFFINE_TRANSFORMATION | ";
+    }
+    if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) {
+        result += "EXTERNAL_STYLUS_PRESENCE | ";
+    }
+    if (changes & CHANGE_ENABLED_STATE) {
+        result += "ENABLED_STATE | ";
+    }
+    if (changes & CHANGE_MUST_REOPEN) {
+        result += "MUST_REOPEN | ";
+    }
+    return result;
+}
+
 std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId(
         const std::string& uniqueDisplayId) const {
     if (uniqueDisplayId.empty()) {
@@ -76,8 +100,10 @@
     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);
+        if (currentViewport.type == type) {
+            if (!result) {
+                result = std::make_optional(currentViewport);
+            }
             count++;
         }
     }
@@ -99,6 +125,16 @@
     return std::nullopt;
 }
 
+std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportById(
+        int32_t displayId) const {
+    for (const DisplayViewport& currentViewport : mDisplays) {
+        if (currentViewport.displayId == displayId) {
+            return std::make_optional(currentViewport);
+        }
+    }
+    return std::nullopt;
+}
+
 void InputReaderConfiguration::setDisplayViewports(const std::vector<DisplayViewport>& viewports) {
     mDisplays = viewports;
 }
@@ -125,4 +161,4 @@
     y = newY;
 }
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
new file mode 100644
index 0000000..b87f7a1
--- /dev/null
+++ b/services/inputflinger/InputThread.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "InputThread.h"
+
+namespace android {
+
+namespace {
+
+// Implementation of Thread from libutils.
+class InputThreadImpl : public Thread {
+public:
+    explicit InputThreadImpl(std::function<void()> loop)
+          : Thread(/* canCallJava */ true), mThreadLoop(loop) {}
+
+    ~InputThreadImpl() {}
+
+private:
+    std::function<void()> mThreadLoop;
+
+    bool threadLoop() override {
+        mThreadLoop();
+        return true;
+    }
+};
+
+} // namespace
+
+InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
+      : mName(name), mThreadWake(wake) {
+    mThread = new InputThreadImpl(loop);
+    mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+}
+
+InputThread::~InputThread() {
+    mThread->requestExit();
+    if (mThreadWake) {
+        mThreadWake();
+    }
+    mThread->requestExitAndWait();
+}
+
+bool InputThread::isCallingThread() {
+    return gettid() == mThread->getTid();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
new file mode 100644
index 0000000..385b981
--- /dev/null
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -0,0 +1,22 @@
+cc_benchmark {
+    name: "inputflinger_benchmarks",
+    srcs: [
+        "InputDispatcher_benchmarks.cpp",
+    ],
+    defaults: ["inputflinger_defaults"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libinput",
+        "libinputflinger_base",
+        "libinputreporter",
+        "liblog",
+        "libstatslog",
+        "libui",
+        "libutils",
+    ],
+    static_libs: [
+        "libinputdispatcher",
+    ],
+}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
new file mode 100644
index 0000000..9a6ef21
--- /dev/null
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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 <benchmark/benchmark.h>
+
+#include <binder/Binder.h>
+#include "../dispatcher/InputDispatcher.h"
+
+namespace android::inputdispatcher {
+
+// An arbitrary device id.
+static const int32_t DEVICE_ID = 1;
+
+// An arbitrary injector pid / uid pair that has permission to inject events.
+static const int32_t INJECTOR_PID = 999;
+static const int32_t INJECTOR_UID = 1001;
+
+static const int32_t INJECT_EVENT_TIMEOUT = 5000;
+static const int32_t DISPATCHING_TIMEOUT = 100000;
+
+static nsecs_t now() {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+// --- FakeInputDispatcherPolicy ---
+
+class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+public:
+    FakeInputDispatcherPolicy() {}
+
+protected:
+    virtual ~FakeInputDispatcherPolicy() {}
+
+private:
+    virtual void notifyConfigurationChanged(nsecs_t) override {}
+
+    virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&, const sp<IBinder>&,
+                              const std::string& name) override {
+        ALOGE("The window is not responding : %s", name.c_str());
+        return 0;
+    }
+
+    virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+    virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+    virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+        *outConfig = mConfig;
+    }
+
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+        return true;
+    }
+
+    virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+
+    virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+
+    virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
+                                                  uint32_t) override {
+        return 0;
+    }
+
+    virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
+                                      KeyEvent*) override {
+        return false;
+    }
+
+    virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+
+    virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+
+    virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
+        return false;
+    }
+
+    virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+
+    InputDispatcherConfiguration mConfig;
+};
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+    FakeApplicationHandle() {}
+    virtual ~FakeApplicationHandle() {}
+
+    virtual bool updateInfo() {
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        return true;
+    }
+};
+
+class FakeInputReceiver {
+public:
+    void consumeEvent() {
+        uint32_t consumeSeq;
+        InputEvent* event;
+
+        std::chrono::time_point start = std::chrono::steady_clock::now();
+        status_t result = WOULD_BLOCK;
+        while (result == WOULD_BLOCK) {
+            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+            if (elapsed > 10ms) {
+                ALOGE("Waited too long for consumer to produce an event, giving up");
+                break;
+            }
+            result = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
+                                        &event);
+        }
+        if (result != OK) {
+            ALOGE("Received result = %d from consume()", result);
+        }
+        result = mConsumer->sendFinishedSignal(consumeSeq, true);
+        if (result != OK) {
+            ALOGE("Received result = %d from sendFinishedSignal", result);
+        }
+    }
+
+protected:
+    explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
+          : mDispatcher(dispatcher) {
+        InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+        mConsumer = std::make_unique<InputConsumer>(mClientChannel);
+    }
+
+    virtual ~FakeInputReceiver() {}
+
+    sp<InputDispatcher> mDispatcher;
+    sp<InputChannel> mServerChannel, mClientChannel;
+    std::unique_ptr<InputConsumer> mConsumer;
+    PreallocatedInputEventFactory mEventFactory;
+};
+
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+    static const int32_t WIDTH = 200;
+    static const int32_t HEIGHT = 200;
+
+    FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+                     const sp<InputDispatcher>& dispatcher, const std::string name)
+          : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
+        mDispatcher->registerInputChannel(mServerChannel);
+
+        inputApplicationHandle->updateInfo();
+        mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+    }
+
+    virtual bool updateInfo() override {
+        mInfo.token = mServerChannel->getConnectionToken();
+        mInfo.name = "FakeWindowHandle";
+        mInfo.layoutParamsFlags = 0;
+        mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+        mInfo.frameLeft = mFrame.left;
+        mInfo.frameTop = mFrame.top;
+        mInfo.frameRight = mFrame.right;
+        mInfo.frameBottom = mFrame.bottom;
+        mInfo.globalScaleFactor = 1.0;
+        mInfo.touchableRegion.clear();
+        mInfo.addTouchableRegion(mFrame);
+        mInfo.visible = true;
+        mInfo.canReceiveKeys = true;
+        mInfo.hasFocus = true;
+        mInfo.hasWallpaper = false;
+        mInfo.paused = false;
+        mInfo.ownerPid = INJECTOR_PID;
+        mInfo.ownerUid = INJECTOR_UID;
+        mInfo.inputFeatures = 0;
+        mInfo.displayId = ADISPLAY_ID_DEFAULT;
+
+        return true;
+    }
+
+protected:
+    Rect mFrame;
+};
+
+static MotionEvent generateMotionEvent() {
+    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, 100);
+
+    const nsecs_t currentTime = now();
+
+    MotionEvent event;
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0,
+                     /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                     1 /* xScale */, 1 /* yScale */,
+                     /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+                     /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    return event;
+}
+
+static NotifyMotionArgs generateMotionArgs() {
+    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, 100);
+
+    const nsecs_t currentTime = now();
+    // Define a valid motion event.
+    NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+                          ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
+                          /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
+                          MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                          pointerProperties, pointerCoords,
+                          /* xPrecision */ 0, /* yPrecision */ 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
+
+    return args;
+}
+
+static void benchmarkNotifyMotion(benchmark::State& state) {
+    // Create dispatcher
+    sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+    sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+    dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+    dispatcher->start();
+
+    // Create a window that will receive motion events
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+    dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyMotionArgs motionArgs = generateMotionArgs();
+
+    for (auto _ : state) {
+        // Send ACTION_DOWN
+        motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
+        motionArgs.sequenceNum = 0;
+        motionArgs.downTime = now();
+        motionArgs.eventTime = motionArgs.downTime;
+        dispatcher->notifyMotion(&motionArgs);
+
+        // Send ACTION_UP
+        motionArgs.action = AMOTION_EVENT_ACTION_UP;
+        motionArgs.sequenceNum = 1;
+        motionArgs.eventTime = now();
+        dispatcher->notifyMotion(&motionArgs);
+
+        window->consumeEvent();
+        window->consumeEvent();
+    }
+
+    dispatcher->stop();
+}
+
+static void benchmarkInjectMotion(benchmark::State& state) {
+    // Create dispatcher
+    sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+    sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+    dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+    dispatcher->start();
+
+    // Create a window that will receive motion events
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+    dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    for (auto _ : state) {
+        MotionEvent event = generateMotionEvent();
+        // Send ACTION_DOWN
+        dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                     INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+                                     POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+
+        // Send ACTION_UP
+        event.setAction(AMOTION_EVENT_ACTION_UP);
+        dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+                                     INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+                                     POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+
+        window->consumeEvent();
+        window->consumeEvent();
+    }
+
+    dispatcher->stop();
+}
+
+BENCHMARK(benchmarkNotifyMotion);
+BENCHMARK(benchmarkInjectMotion);
+
+} // namespace android::inputdispatcher
+
+BENCHMARK_MAIN();
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index b8c3a80..3f956a8 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -21,7 +21,6 @@
         "InjectionState.cpp",
         "InputDispatcher.cpp",
         "InputDispatcherFactory.cpp",
-        "InputDispatcherThread.cpp",
         "InputState.cpp",
         "InputTarget.cpp",
         "Monitor.cpp",
@@ -29,11 +28,13 @@
     ],
     shared_libs: [
         "libbase",
+        "libcrypto",
         "libcutils",
         "libinput",
         "libinputreporter",
         "libinputflinger_base",
         "liblog",
+        "libstatslog",
         "libui",
         "libutils",
     ],
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index ef7f650..6f82f4f 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -52,13 +52,13 @@
     }
 }
 
-DispatchEntry* Connection::findWaitQueueEntry(uint32_t seq) {
-    for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) {
-        if (entry->seq == seq) {
-            return entry;
+std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
+    for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
+        if ((*it)->seq == seq) {
+            return it;
         }
     }
-    return nullptr;
+    return waitQueue.end();
 }
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index ed4eebd..8423010 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -18,7 +18,6 @@
 #define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H
 
 #include "InputState.h"
-#include "Queue.h"
 
 #include <input/InputTransport.h>
 #include <deque>
@@ -53,11 +52,11 @@
     bool inputPublisherBlocked;
 
     // Queue of events that need to be published to the connection.
-    Queue<DispatchEntry> outboundQueue;
+    std::deque<DispatchEntry*> outboundQueue;
 
     // Queue of events that have been published to the connection but that have not
     // yet received a "finished" response from the application.
-    Queue<DispatchEntry> waitQueue;
+    std::deque<DispatchEntry*> waitQueue;
 
     explicit Connection(const sp<InputChannel>& inputChannel, bool monitor);
 
@@ -66,7 +65,7 @@
     const std::string getWindowName() const;
     const char* getStatusLabel() const;
 
-    DispatchEntry* findWaitQueueEntry(uint32_t seq);
+    std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
 };
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a4d9e4e..b723654 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -60,7 +60,7 @@
 
 // --- EventEntry ---
 
-EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags)
+EventEntry::EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags)
       : sequenceNum(sequenceNum),
         refCount(1),
         type(type),
@@ -92,7 +92,7 @@
 // --- ConfigurationChangedEntry ---
 
 ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime)
-      : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {}
+      : EventEntry(sequenceNum, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
 
 ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
 
@@ -103,7 +103,7 @@
 // --- DeviceResetEntry ---
 
 DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId)
-      : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
+      : EventEntry(sequenceNum, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
 
 DeviceResetEntry::~DeviceResetEntry() {}
 
@@ -111,13 +111,28 @@
     msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags);
 }
 
+// --- FocusEntry ---
+
+// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+FocusEntry::FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp<IBinder> connectionToken,
+                       bool hasFocus)
+      : EventEntry(sequenceNum, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
+        connectionToken(connectionToken),
+        hasFocus(hasFocus) {}
+
+FocusEntry::~FocusEntry() {}
+
+void FocusEntry::appendDescription(std::string& msg) const {
+    msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false");
+}
+
 // --- KeyEntry ---
 
 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(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+      : EventEntry(sequenceNum, Type::KEY, eventTime, policyFlags),
         deviceId(deviceId),
         source(source),
         displayId(displayId),
@@ -161,10 +176,11 @@
                          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,
+                         int32_t edgeFlags, float xPrecision, float yPrecision,
+                         float xCursorPosition, float yCursorPosition, nsecs_t downTime,
                          uint32_t pointerCount, const PointerProperties* pointerProperties,
                          const PointerCoords* pointerCoords, float xOffset, float yOffset)
-      : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
+      : EventEntry(sequenceNum, Type::MOTION, eventTime, policyFlags),
         eventTime(eventTime),
         deviceId(deviceId),
         source(source),
@@ -178,6 +194,8 @@
         edgeFlags(edgeFlags),
         xPrecision(xPrecision),
         yPrecision(yPrecision),
+        xCursorPosition(xCursorPosition),
+        yCursorPosition(yCursorPosition),
         downTime(downTime),
         pointerCount(pointerCount) {
     for (uint32_t i = 0; i < pointerCount; i++) {
@@ -200,11 +218,11 @@
                         ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
                         "buttonState=0x%08x, "
                         "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
-                        "pointers=[",
+                        "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
                         deviceId, source, displayId, motionActionToString(action).c_str(),
                         actionButton, flags, metaState, buttonState,
                         motionClassificationToString(classification), edgeFlags, xPrecision,
-                        yPrecision);
+                        yPrecision, xCursorPosition, yCursorPosition);
 
     for (uint32_t i = 0; i < pointerCount; i++) {
         if (i) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index b904caf..e8c37f0 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -29,35 +29,65 @@
 
 namespace android::inputdispatcher {
 
-template <typename T>
-struct Link {
-    T* next;
-    T* prev;
+// Sequence number for synthesized or injected events.
+constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
 
-protected:
-    inline Link() : next(nullptr), prev(nullptr) {}
-};
+struct EventEntry {
+    enum class Type {
+        CONFIGURATION_CHANGED,
+        DEVICE_RESET,
+        FOCUS,
+        KEY,
+        MOTION,
+    };
 
-struct EventEntry : Link<EventEntry> {
-    enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION };
+    static const char* typeToString(Type type) {
+        switch (type) {
+            case Type::CONFIGURATION_CHANGED:
+                return "CONFIGURATION_CHANGED";
+            case Type::DEVICE_RESET:
+                return "DEVICE_RESET";
+            case Type::FOCUS:
+                return "FOCUS";
+            case Type::KEY:
+                return "KEY";
+            case Type::MOTION:
+                return "MOTION";
+        }
+    }
 
     uint32_t sequenceNum;
     mutable int32_t refCount;
-    int32_t type;
+    Type type;
     nsecs_t eventTime;
     uint32_t policyFlags;
     InjectionState* injectionState;
 
     bool dispatchInProgress; // initially false, set to true while dispatching
 
+    /**
+     * Injected keys are events from an external (probably untrusted) application
+     * and are not related to real hardware state. They come in via
+     * InputDispatcher::injectInputEvent, which sets policy flag POLICY_FLAG_INJECTED.
+     */
     inline bool isInjected() const { return injectionState != nullptr; }
 
+    /**
+     * Synthesized events are either injected events, or events that come
+     * from real hardware, but aren't directly attributable to a specific hardware event.
+     * Key repeat is a synthesized event, because it is related to an actual hardware state
+     * (a key is currently pressed), but the repeat itself is generated by the framework.
+     */
+    inline bool isSynthesized() const {
+        return isInjected() || sequenceNum == SYNTHESIZED_EVENT_SEQUENCE_NUM;
+    }
+
     void release();
 
     virtual void appendDescription(std::string& msg) const = 0;
 
 protected:
-    EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+    EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags);
     virtual ~EventEntry();
     void releaseInjectionState();
 };
@@ -80,6 +110,17 @@
     virtual ~DeviceResetEntry();
 };
 
+struct FocusEntry : EventEntry {
+    sp<IBinder> connectionToken;
+    bool hasFocus;
+
+    FocusEntry(uint32_t sequenceNum, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus);
+    virtual void appendDescription(std::string& msg) const;
+
+protected:
+    virtual ~FocusEntry();
+};
+
 struct KeyEntry : EventEntry {
     int32_t deviceId;
     uint32_t source;
@@ -128,6 +169,8 @@
     int32_t edgeFlags;
     float xPrecision;
     float yPrecision;
+    float xCursorPosition;
+    float yCursorPosition;
     nsecs_t downTime;
     uint32_t pointerCount;
     PointerProperties pointerProperties[MAX_POINTERS];
@@ -137,9 +180,9 @@
                 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);
+                float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime,
+                uint32_t pointerCount, const PointerProperties* pointerProperties,
+                const PointerCoords* pointerCoords, float xOffset, float yOffset);
     virtual void appendDescription(std::string& msg) const;
 
 protected:
@@ -147,7 +190,7 @@
 };
 
 // Tracks the progress of dispatching a particular event to a particular connection.
-struct DispatchEntry : Link<DispatchEntry> {
+struct DispatchEntry {
     const uint32_t seq; // unique sequence number, never 0
 
     EventEntry* eventEntry; // the event to dispatch
@@ -195,10 +238,10 @@
 //
 // Commands are implicitly 'LockedInterruptible'.
 struct CommandEntry;
-typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry);
+typedef std::function<void(InputDispatcher&, CommandEntry*)> Command;
 
 class Connection;
-struct CommandEntry : Link<CommandEntry> {
+struct CommandEntry {
     explicit CommandEntry(Command command);
     ~CommandEntry();
 
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 4410008..75bc0aa 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -17,7 +17,7 @@
 #define LOG_TAG "InputDispatcher"
 #define ATRACE_TAG ATRACE_TAG_INPUT
 
-#define LOG_NDEBUG 0
+#define LOG_NDEBUG 1
 
 // Log detailed debug messages about each inbound event notification to the dispatcher.
 #define DEBUG_INBOUND_EVENT_DETAILS 0
@@ -35,7 +35,7 @@
 #define DEBUG_INJECTION 0
 
 // Log debug messages about input focus tracking.
-#define DEBUG_FOCUS 0
+static constexpr bool DEBUG_FOCUS = false;
 
 // Log debug messages about the app switch latency optimization.
 #define DEBUG_APP_SWITCH 0
@@ -50,15 +50,20 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <statslog.h>
 #include <stddef.h>
 #include <time.h>
 #include <unistd.h>
+#include <queue>
 #include <sstream>
 
 #include <android-base/chrono_utils.h>
 #include <android-base/stringprintf.h>
 #include <binder/Binder.h>
+#include <input/InputDevice.h>
 #include <log/log.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
 #include <powermanager/PowerManager.h>
 #include <utils/Trace.h>
 
@@ -203,10 +208,173 @@
     }
 }
 
-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{};
+/**
+ * Find the entry in std::unordered_map by key, and return it.
+ * If the entry is not found, return a default constructed entry.
+ *
+ * Useful when the entries are vectors, since an empty vector will be returned
+ * if the entry is not found.
+ * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned.
+ */
+template <typename K, typename V>
+static V getValueByKey(const std::unordered_map<K, V>& map, K key) {
+    auto it = map.find(key);
+    return it != map.end() ? it->second : V{};
+}
+
+/**
+ * Find the entry in std::unordered_map by value, and remove it.
+ * If more than one entry has the same value, then all matching
+ * key-value pairs will be removed.
+ *
+ * Return true if at least one value has been removed.
+ */
+template <typename K, typename V>
+static bool removeByValue(std::unordered_map<K, V>& map, const V& value) {
+    bool removed = false;
+    for (auto it = map.begin(); it != map.end();) {
+        if (it->second == value) {
+            it = map.erase(it);
+            removed = true;
+        } else {
+            it++;
+        }
+    }
+    return removed;
+}
+
+static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
+    if (first == second) {
+        return true;
+    }
+
+    if (first == nullptr || second == nullptr) {
+        return false;
+    }
+
+    return first->getToken() == second->getToken();
+}
+
+static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
+    return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
+}
+
+static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+                                                          EventEntry* eventEntry,
+                                                          int32_t inputTargetFlags) {
+    if (inputTarget.useDefaultPointerInfo()) {
+        const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+        return std::make_unique<DispatchEntry>(eventEntry, // increments ref
+                                               inputTargetFlags, pointerInfo.xOffset,
+                                               pointerInfo.yOffset, inputTarget.globalScaleFactor,
+                                               pointerInfo.windowXScale, pointerInfo.windowYScale);
+    }
+
+    ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
+    const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+
+    PointerCoords pointerCoords[motionEntry.pointerCount];
+
+    // Use the first pointer information to normalize all other pointers. This could be any pointer
+    // as long as all other pointers are normalized to the same value and the final DispatchEntry
+    // uses the offset and scale for the normalized pointer.
+    const PointerInfo& firstPointerInfo =
+            inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+
+    // Iterate through all pointers in the event to normalize against the first.
+    for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
+        const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
+        uint32_t pointerId = uint32_t(pointerProperties.id);
+        const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
+
+        // The scale factor is the ratio of the current pointers scale to the normalized scale.
+        float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
+        float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+
+        pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
+        // First apply the current pointers offset to set the window at 0,0
+        pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
+        // Next scale the coordinates.
+        pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
+        // Lastly, offset the coordinates so they're in the normalized pointer's frame.
+        pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
+                                                -firstPointerInfo.yOffset);
+    }
+
+    MotionEntry* combinedMotionEntry =
+            new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId,
+                            motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
+                            motionEntry.action, motionEntry.actionButton, motionEntry.flags,
+                            motionEntry.metaState, motionEntry.buttonState,
+                            motionEntry.classification, motionEntry.edgeFlags,
+                            motionEntry.xPrecision, motionEntry.yPrecision,
+                            motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+                            motionEntry.downTime, motionEntry.pointerCount,
+                            motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
+                            0 /* yOffset */);
+
+    if (motionEntry.injectionState) {
+        combinedMotionEntry->injectionState = motionEntry.injectionState;
+        combinedMotionEntry->injectionState->refCount += 1;
+    }
+
+    std::unique_ptr<DispatchEntry> dispatchEntry =
+            std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
+                                            inputTargetFlags, firstPointerInfo.xOffset,
+                                            firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
+                                            firstPointerInfo.windowXScale,
+                                            firstPointerInfo.windowYScale);
+    combinedMotionEntry->release();
+    return dispatchEntry;
+}
+
+static std::array<uint8_t, 128> getRandomKey() {
+    std::array<uint8_t, 128> key;
+    if (RAND_bytes(key.data(), key.size()) != 1) {
+        LOG_ALWAYS_FATAL("Can't generate HMAC key");
+    }
+    return key;
+}
+
+// --- HmacKeyManager ---
+
+HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const {
+    size_t size;
+    switch (event.type) {
+        case VerifiedInputEvent::Type::KEY: {
+            size = sizeof(VerifiedKeyEvent);
+            break;
+        }
+        case VerifiedInputEvent::Type::MOTION: {
+            size = sizeof(VerifiedMotionEvent);
+            break;
+        }
+    }
+    std::vector<uint8_t> data;
+    const uint8_t* start = reinterpret_cast<const uint8_t*>(&event);
+    data.assign(start, start + size);
+    return sign(data);
+}
+
+std::array<uint8_t, 32> HmacKeyManager::sign(const std::vector<uint8_t>& data) const {
+    // SHA256 always generates 32-bytes result
+    std::array<uint8_t, 32> hash;
+    unsigned int hashLen = 0;
+    uint8_t* result = HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data.data(), data.size(),
+                           hash.data(), &hashLen);
+    if (result == nullptr) {
+        ALOGE("Could not sign the data using HMAC");
+        return INVALID_HMAC;
+    }
+
+    if (hashLen != hash.size()) {
+        ALOGE("HMAC-SHA256 has unexpected length");
+        return INVALID_HMAC;
+    }
+
+    return hash;
 }
 
 // --- InputDispatcher ---
@@ -214,13 +382,17 @@
 InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
       : mPolicy(policy),
         mPendingEvent(nullptr),
-        mLastDropReason(DROP_REASON_NOT_DROPPED),
+        mLastDropReason(DropReason::NOT_DROPPED),
         mAppSwitchSawKeyDown(false),
         mAppSwitchDueTime(LONG_LONG_MAX),
         mNextUnblockedEvent(nullptr),
         mDispatchEnabled(false),
         mDispatchFrozen(false),
         mInputFilterEnabled(false),
+        // mInTouchMode will be initialized by the WindowManager to the default device config.
+        // To avoid leaking stack in case that call never comes, and for tests,
+        // initialize it here anyways.
+        mInTouchMode(true),
         mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
         mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
     mLooper = new Looper(false);
@@ -240,11 +412,30 @@
         drainInboundQueueLocked();
     }
 
-    while (mConnectionsByFd.size() != 0) {
-        unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel);
+    while (!mConnectionsByFd.empty()) {
+        sp<Connection> connection = mConnectionsByFd.begin()->second;
+        unregisterInputChannel(connection->inputChannel);
     }
 }
 
+status_t InputDispatcher::start() {
+    if (mThread) {
+        return ALREADY_EXISTS;
+    }
+    mThread = std::make_unique<InputThread>(
+            "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
+    return OK;
+}
+
+status_t InputDispatcher::stop() {
+    if (mThread && mThread->isCallingThread()) {
+        ALOGE("InputDispatcher cannot be stopped from its own thread!");
+        return INVALID_OPERATION;
+    }
+    mThread.reset();
+    return OK;
+}
+
 void InputDispatcher::dispatchOnce() {
     nsecs_t nextWakeupTime = LONG_LONG_MAX;
     { // acquire lock
@@ -262,6 +453,12 @@
         if (runCommandsLockedInterruptible()) {
             nextWakeupTime = LONG_LONG_MIN;
         }
+
+        // We are about to enter an infinitely long sleep, because we have no commands or
+        // pending or queued events
+        if (nextWakeupTime == LONG_LONG_MAX) {
+            mDispatcherEnteredIdle.notify_all();
+        }
     } // release lock
 
     // Wait for callback or timeout or wake.  (make sure we round up, not down)
@@ -282,9 +479,9 @@
 
     // If dispatching is frozen, do not process timeouts or try to deliver any new events.
     if (mDispatchFrozen) {
-#if DEBUG_FOCUS
-        ALOGD("Dispatch frozen.  Waiting some more.");
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGD("Dispatch frozen.  Waiting some more.");
+        }
         return;
     }
 
@@ -299,7 +496,7 @@
     // Ready to start a new event.
     // If we don't already have a pending event, go grab one.
     if (!mPendingEvent) {
-        if (mInboundQueue.isEmpty()) {
+        if (mInboundQueue.empty()) {
             if (isAppSwitchDue) {
                 // The inbound queue is empty so the app switch key we were waiting
                 // for will never arrive.  Stop waiting for it.
@@ -324,13 +521,14 @@
             }
         } else {
             // Inbound queue has at least one entry.
-            mPendingEvent = mInboundQueue.dequeueAtHead();
+            mPendingEvent = mInboundQueue.front();
+            mInboundQueue.pop_front();
             traceInboundQueueLengthLocked();
         }
 
         // Poke user activity for this event.
         if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
-            pokeUserActivityLocked(mPendingEvent);
+            pokeUserActivityLocked(*mPendingEvent);
         }
 
         // Get ready to dispatch the event.
@@ -341,11 +539,11 @@
     // All events are eventually dequeued and processed this way, even if we intend to drop them.
     ALOG_ASSERT(mPendingEvent != nullptr);
     bool done = false;
-    DropReason dropReason = DROP_REASON_NOT_DROPPED;
+    DropReason dropReason = DropReason::NOT_DROPPED;
     if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
-        dropReason = DROP_REASON_POLICY;
+        dropReason = DropReason::POLICY;
     } else if (!mDispatchEnabled) {
-        dropReason = DROP_REASON_DISABLED;
+        dropReason = DropReason::DISABLED;
     }
 
     if (mNextUnblockedEvent == mPendingEvent) {
@@ -353,64 +551,68 @@
     }
 
     switch (mPendingEvent->type) {
-        case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+        case EventEntry::Type::CONFIGURATION_CHANGED: {
             ConfigurationChangedEntry* typedEntry =
                     static_cast<ConfigurationChangedEntry*>(mPendingEvent);
             done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
-            dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
+            dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped
             break;
         }
 
-        case EventEntry::TYPE_DEVICE_RESET: {
+        case EventEntry::Type::DEVICE_RESET: {
             DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
             done = dispatchDeviceResetLocked(currentTime, typedEntry);
-            dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
+            dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
             break;
         }
 
-        case EventEntry::TYPE_KEY: {
+        case EventEntry::Type::FOCUS: {
+            FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent);
+            dispatchFocusLocked(currentTime, typedEntry);
+            done = true;
+            dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
+            break;
+        }
+
+        case EventEntry::Type::KEY: {
             KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
             if (isAppSwitchDue) {
-                if (isAppSwitchKeyEvent(typedEntry)) {
+                if (isAppSwitchKeyEvent(*typedEntry)) {
                     resetPendingAppSwitchLocked(true);
                     isAppSwitchDue = false;
-                } else if (dropReason == DROP_REASON_NOT_DROPPED) {
-                    dropReason = DROP_REASON_APP_SWITCH;
+                } else if (dropReason == DropReason::NOT_DROPPED) {
+                    dropReason = DropReason::APP_SWITCH;
                 }
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
-                dropReason = DROP_REASON_STALE;
+            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+                dropReason = DropReason::STALE;
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
-                dropReason = DROP_REASON_BLOCKED;
+            if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
+                dropReason = DropReason::BLOCKED;
             }
             done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
             break;
         }
 
-        case EventEntry::TYPE_MOTION: {
+        case EventEntry::Type::MOTION: {
             MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
-            if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
-                dropReason = DROP_REASON_APP_SWITCH;
+            if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
+                dropReason = DropReason::APP_SWITCH;
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) {
-                dropReason = DROP_REASON_STALE;
+            if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) {
+                dropReason = DropReason::STALE;
             }
-            if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
-                dropReason = DROP_REASON_BLOCKED;
+            if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) {
+                dropReason = DropReason::BLOCKED;
             }
             done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
             break;
         }
-
-        default:
-            ALOG_ASSERT(false);
-            break;
     }
 
     if (done) {
-        if (dropReason != DROP_REASON_NOT_DROPPED) {
-            dropInboundEventLocked(mPendingEvent, dropReason);
+        if (dropReason != DropReason::NOT_DROPPED) {
+            dropInboundEventLocked(*mPendingEvent, dropReason);
         }
         mLastDropReason = dropReason;
 
@@ -420,25 +622,25 @@
 }
 
 bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
-    bool needWake = mInboundQueue.isEmpty();
-    mInboundQueue.enqueueAtTail(entry);
+    bool needWake = mInboundQueue.empty();
+    mInboundQueue.push_back(entry);
     traceInboundQueueLengthLocked();
 
     switch (entry->type) {
-        case EventEntry::TYPE_KEY: {
+        case EventEntry::Type::KEY: {
             // Optimize app switch latency.
             // If the application takes too long to catch up then we drop all events preceding
             // the app switch key.
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry);
             if (isAppSwitchKeyEvent(keyEntry)) {
-                if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
+                if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) {
                     mAppSwitchSawKeyDown = true;
-                } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
+                } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) {
                     if (mAppSwitchSawKeyDown) {
 #if DEBUG_APP_SWITCH
                         ALOGD("App switch is pending!");
 #endif
-                        mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
+                        mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT;
                         mAppSwitchSawKeyDown = false;
                         needWake = true;
                     }
@@ -447,7 +649,7 @@
             break;
         }
 
-        case EventEntry::TYPE_MOTION: {
+        case EventEntry::Type::MOTION: {
             // Optimize case where the current application is unresponsive and the user
             // decides to touch a window in a different application.
             // If the application takes too long to catch up then we drop all events preceding
@@ -475,6 +677,12 @@
             }
             break;
         }
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET:
+        case EventEntry::Type::FOCUS: {
+            // nothing to do
+            break;
+        }
     }
 
     return needWake;
@@ -482,9 +690,10 @@
 
 void InputDispatcher::addRecentEventLocked(EventEntry* entry) {
     entry->refCount += 1;
-    mRecentQueue.enqueueAtTail(entry);
-    if (mRecentQueue.count() > RECENT_QUEUE_MAX_SIZE) {
-        mRecentQueue.dequeueAtHead()->release();
+    mRecentQueue.push_back(entry);
+    if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) {
+        mRecentQueue.front()->release();
+        mRecentQueue.pop_front();
     }
 }
 
@@ -557,49 +766,50 @@
     }
 }
 
-void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) {
+void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
     const char* reason;
     switch (dropReason) {
-        case DROP_REASON_POLICY:
+        case DropReason::POLICY:
 #if DEBUG_INBOUND_EVENT_DETAILS
             ALOGD("Dropped event because policy consumed it.");
 #endif
             reason = "inbound event was dropped because the policy consumed it";
             break;
-        case DROP_REASON_DISABLED:
-            if (mLastDropReason != DROP_REASON_DISABLED) {
+        case DropReason::DISABLED:
+            if (mLastDropReason != DropReason::DISABLED) {
                 ALOGI("Dropped event because input dispatch is disabled.");
             }
             reason = "inbound event was dropped because input dispatch is disabled";
             break;
-        case DROP_REASON_APP_SWITCH:
+        case DropReason::APP_SWITCH:
             ALOGI("Dropped event because of pending overdue app switch.");
             reason = "inbound event was dropped because of pending overdue app switch";
             break;
-        case DROP_REASON_BLOCKED:
+        case DropReason::BLOCKED:
             ALOGI("Dropped event because the current application is not responding and the user "
                   "has started interacting with a different application.");
             reason = "inbound event was dropped because the current application is not responding "
                      "and the user has started interacting with a different application";
             break;
-        case DROP_REASON_STALE:
+        case DropReason::STALE:
             ALOGI("Dropped event because it is stale.");
             reason = "inbound event was dropped because it is stale";
             break;
-        default:
-            ALOG_ASSERT(false);
+        case DropReason::NOT_DROPPED: {
+            LOG_ALWAYS_FATAL("Should not be dropping a NOT_DROPPED event");
             return;
+        }
     }
 
-    switch (entry->type) {
-        case EventEntry::TYPE_KEY: {
+    switch (entry.type) {
+        case EventEntry::Type::KEY: {
             CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
             synthesizeCancelationEventsForAllConnectionsLocked(options);
             break;
         }
-        case EventEntry::TYPE_MOTION: {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
-            if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+            if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
                 CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
                 synthesizeCancelationEventsForAllConnectionsLocked(options);
             } else {
@@ -608,6 +818,12 @@
             }
             break;
         }
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type));
+            break;
+        }
     }
 }
 
@@ -616,10 +832,10 @@
             keyCode == AKEYCODE_APP_SWITCH;
 }
 
-bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) {
-    return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) &&
-            (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) &&
-            (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER);
+bool InputDispatcher::isAppSwitchKeyEvent(const KeyEntry& keyEntry) {
+    return !(keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry.keyCode) &&
+            (keyEntry.policyFlags & POLICY_FLAG_TRUSTED) &&
+            (keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER);
 }
 
 bool InputDispatcher::isAppSwitchPendingLocked() {
@@ -638,40 +854,34 @@
 #endif
 }
 
-bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) {
-    return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT;
-}
-
 bool InputDispatcher::haveCommandsLocked() const {
-    return !mCommandQueue.isEmpty();
+    return !mCommandQueue.empty();
 }
 
 bool InputDispatcher::runCommandsLockedInterruptible() {
-    if (mCommandQueue.isEmpty()) {
+    if (mCommandQueue.empty()) {
         return false;
     }
 
     do {
-        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
-
+        std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front());
+        mCommandQueue.pop_front();
         Command command = commandEntry->command;
-        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'
+        command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible'
 
         commandEntry->connection.clear();
-        delete commandEntry;
-    } while (!mCommandQueue.isEmpty());
+    } while (!mCommandQueue.empty());
     return true;
 }
 
-CommandEntry* InputDispatcher::postCommandLocked(Command command) {
-    CommandEntry* commandEntry = new CommandEntry(command);
-    mCommandQueue.enqueueAtTail(commandEntry);
-    return commandEntry;
+void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) {
+    mCommandQueue.push_back(std::move(commandEntry));
 }
 
 void InputDispatcher::drainInboundQueueLocked() {
-    while (!mInboundQueue.isEmpty()) {
-        EventEntry* entry = mInboundQueue.dequeueAtHead();
+    while (!mInboundQueue.empty()) {
+        EventEntry* entry = mInboundQueue.front();
+        mInboundQueue.pop_front();
         releaseInboundEventLocked(entry);
     }
     traceInboundQueueLengthLocked();
@@ -750,9 +960,10 @@
     resetKeyRepeatLocked();
 
     // Enqueue a command to run outside the lock to tell the policy that the configuration changed.
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible);
     commandEntry->eventTime = entry->eventTime;
+    postCommandLocked(std::move(commandEntry));
     return true;
 }
 
@@ -768,6 +979,25 @@
     return true;
 }
 
+void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) {
+    FocusEntry* focusEntry =
+            new FocusEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, now(), window.getToken(), hasFocus);
+    enqueueInboundEventLocked(focusEntry);
+}
+
+void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) {
+    sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+    if (channel == nullptr) {
+        return; // Window has gone away
+    }
+    InputTarget target;
+    target.inputChannel = channel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+    entry->dispatchInProgress = true;
+
+    dispatchEventLocked(currentTime, entry, {target});
+}
+
 bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
                                         DropReason* dropReason, nsecs_t* nextWakeupTime) {
     // Preprocessing.
@@ -803,7 +1033,7 @@
 
         entry->dispatchInProgress = true;
 
-        logOutboundKeyDetails("dispatchKey - ", entry);
+        logOutboundKeyDetails("dispatchKey - ", *entry);
     }
 
     // Handle case where the policy asked us to try again later last time.
@@ -821,29 +1051,30 @@
     // Give the policy a chance to intercept the key.
     if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
         if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
-            CommandEntry* commandEntry = postCommandLocked(
+            std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                     &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
             sp<InputWindowHandle> focusedWindowHandle =
-                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry));
+                    getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry));
             if (focusedWindowHandle != nullptr) {
                 commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken());
             }
             commandEntry->keyEntry = entry;
+            postCommandLocked(std::move(commandEntry));
             entry->refCount += 1;
             return false; // wait for the command to run
         } else {
             entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
         }
     } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
-        if (*dropReason == DROP_REASON_NOT_DROPPED) {
-            *dropReason = DROP_REASON_POLICY;
+        if (*dropReason == DropReason::NOT_DROPPED) {
+            *dropReason = DropReason::POLICY;
         }
     }
 
     // Clean up if dropping the event.
-    if (*dropReason != DROP_REASON_NOT_DROPPED) {
+    if (*dropReason != DropReason::NOT_DROPPED) {
         setInjectionResult(entry,
-                           *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
                                                              : INPUT_EVENT_INJECTION_FAILED);
         mReporter->reportDroppedKey(entry->sequenceNum);
         return true;
@@ -852,7 +1083,7 @@
     // Identify targets.
     std::vector<InputTarget> inputTargets;
     int32_t injectionResult =
-            findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
+            findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
         return false;
     }
@@ -863,21 +1094,21 @@
     }
 
     // Add monitor channels from event's or focused display.
-    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
     // Dispatch the key.
     dispatchEventLocked(currentTime, entry, inputTargets);
     return true;
 }
 
-void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) {
+void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     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->displayId,
-          entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode,
-          entry->metaState, entry->repeatCount, entry->downTime);
+          prefix, 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
 }
 
@@ -888,13 +1119,13 @@
     if (!entry->dispatchInProgress) {
         entry->dispatchInProgress = true;
 
-        logOutboundMotionDetails("dispatchMotion - ", entry);
+        logOutboundMotionDetails("dispatchMotion - ", *entry);
     }
 
     // Clean up if dropping the event.
-    if (*dropReason != DROP_REASON_NOT_DROPPED) {
+    if (*dropReason != DropReason::NOT_DROPPED) {
         setInjectionResult(entry,
-                           *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
+                           *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED
                                                              : INPUT_EVENT_INJECTION_FAILED);
         return true;
     }
@@ -909,12 +1140,12 @@
     if (isPointerEvent) {
         // Pointer event.  (eg. touchscreen)
         injectionResult =
-                findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime,
+                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                                &conflictingPointerActions);
     } else {
         // Non touch event.  (eg. trackball)
         injectionResult =
-                findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
+                findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime);
     }
     if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
         return false;
@@ -933,7 +1164,7 @@
     }
 
     // Add monitor channels from event's or focused display.
-    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry));
+    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
 
     if (isPointerEvent) {
         ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(entry->displayId);
@@ -961,33 +1192,32 @@
     return true;
 }
 
-void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) {
+void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
 #if DEBUG_OUTBOUND_EVENT_DETAILS
     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->displayId,
-          entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState,
-          entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision,
-          entry->downTime);
+          prefix, 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, entry.downTime);
 
-    for (uint32_t i = 0; i < entry->pointerCount; i++) {
+    for (uint32_t i = 0; i < entry.pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
               "x=%f, y=%f, pressure=%f, size=%f, "
               "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, "
               "orientation=%f",
-              i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType,
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
-              entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+              i, entry.pointerProperties[i].id, entry.pointerProperties[i].toolType,
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
+              entry.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
     }
 #endif
 }
@@ -1001,32 +1231,32 @@
 
     ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
 
-    pokeUserActivityLocked(eventEntry);
+    pokeUserActivityLocked(*eventEntry);
 
     for (const InputTarget& inputTarget : inputTargets) {
-        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
-        if (connectionIndex >= 0) {
-            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
+        sp<Connection> connection =
+                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
+        if (connection != nullptr) {
+            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
         } else {
-#if DEBUG_FOCUS
-            ALOGD("Dropping event delivery to target with channel '%s' because it "
-                  "is no longer registered with the input dispatcher.",
-                  inputTarget.inputChannel->getName().c_str());
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Dropping event delivery to target with channel '%s' because it "
+                      "is no longer registered with the input dispatcher.",
+                      inputTarget.inputChannel->getName().c_str());
+            }
         }
     }
 }
 
 int32_t InputDispatcher::handleTargetsNotReadyLocked(
-        nsecs_t currentTime, const EventEntry* entry,
+        nsecs_t currentTime, const EventEntry& entry,
         const sp<InputApplicationHandle>& applicationHandle,
         const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) {
     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);
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Waiting for system to become ready for input.  Reason: %s", reason);
+            }
             mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
             mInputTargetWaitStartTime = currentTime;
             mInputTargetWaitTimeoutTime = LONG_LONG_MAX;
@@ -1035,10 +1265,10 @@
         }
     } else {
         if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
-#if DEBUG_FOCUS
-            ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
-                  getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
+                      getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason);
+            }
             nsecs_t timeout;
             if (windowHandle != nullptr) {
                 timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
@@ -1069,7 +1299,7 @@
     }
 
     if (currentTime >= mInputTargetWaitTimeoutTime) {
-        onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime,
+        onANRLocked(currentTime, applicationHandle, windowHandle, entry.eventTime,
                     mInputTargetWaitStartTime, reason);
 
         // Force poll loop to wake up immediately on next iteration once we get the
@@ -1093,7 +1323,7 @@
 }
 
 void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(
-        nsecs_t newTimeout, const sp<InputChannel>& inputChannel) {
+        nsecs_t newTimeout, const sp<IBinder>& inputConnectionToken) {
     if (newTimeout > 0) {
         // Extend the timeout.
         mInputTargetWaitTimeoutTime = now() + newTimeout;
@@ -1102,21 +1332,14 @@
         mInputTargetWaitTimeoutExpired = true;
 
         // Input state will not be realistic.  Mark it out of sync.
-        if (inputChannel.get()) {
-            ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-            if (connectionIndex >= 0) {
-                sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-                sp<IBinder> token = connection->inputChannel->getToken();
+        sp<Connection> connection = getConnectionLocked(inputConnectionToken);
+        if (connection != nullptr) {
+            removeWindowByTokenLocked(inputConnectionToken);
 
-                if (token != nullptr) {
-                    removeWindowByTokenLocked(token);
-                }
-
-                if (connection->status == Connection::STATUS_NORMAL) {
-                    CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
-                                               "application not responding");
-                    synthesizeCancelationEventsForConnectionLocked(connection, options);
-                }
+            if (connection->status == Connection::STATUS_NORMAL) {
+                CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
+                                           "application not responding");
+                synthesizeCancelationEventsForConnectionLocked(connection, options);
             }
         }
     }
@@ -1130,9 +1353,9 @@
 }
 
 void InputDispatcher::resetANRTimeoutsLocked() {
-#if DEBUG_FOCUS
-    ALOGD("Resetting ANR timeouts.");
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("Resetting ANR timeouts.");
+    }
 
     // Reset input target wait timeout.
     mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;
@@ -1144,21 +1367,23 @@
  * 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 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;
+    switch (entry.type) {
+        case EventEntry::Type::KEY: {
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+            displayId = keyEntry.displayId;
             break;
         }
-        case EventEntry::TYPE_MOTION: {
-            const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry);
-            displayId = typedEntry->displayId;
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
+            displayId = motionEntry.displayId;
             break;
         }
-        default: {
-            ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type);
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type));
             return ADISPLAY_ID_NONE;
         }
     }
@@ -1166,7 +1391,7 @@
 }
 
 int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
-                                                        const EventEntry* entry,
+                                                        const EventEntry& entry,
                                                         std::vector<InputTarget>& inputTargets,
                                                         nsecs_t* nextWakeupTime) {
     int32_t injectionResult;
@@ -1186,10 +1411,8 @@
                     handleTargetsNotReadyLocked(currentTime, entry, 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.");
+                                                "a focused application that may eventually add a "
+                                                "window when it finishes starting up.");
             goto Unresponsive;
         }
 
@@ -1201,7 +1424,7 @@
     }
 
     // Check permissions.
-    if (!checkInjectionPermission(focusedWindowHandle, entry->injectionState)) {
+    if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) {
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         goto Failed;
     }
@@ -1226,16 +1449,16 @@
 Unresponsive:
     nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
     updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-#if DEBUG_FOCUS
-    ALOGD("findFocusedWindow finished: injectionResult=%d, "
-          "timeSpentWaitingForApplication=%0.1fms",
-          injectionResult, timeSpentWaitingForApplication / 1000000.0);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("findFocusedWindow finished: injectionResult=%d, "
+              "timeSpentWaitingForApplication=%0.1fms",
+              injectionResult, timeSpentWaitingForApplication / 1000000.0);
+    }
     return injectionResult;
 }
 
 int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
-                                                        const MotionEntry* entry,
+                                                        const MotionEntry& entry,
                                                         std::vector<InputTarget>& inputTargets,
                                                         nsecs_t* nextWakeupTime,
                                                         bool* outConflictingPointerActions) {
@@ -1248,8 +1471,8 @@
 
     // For security reasons, we defer updating the touch state until we are sure that
     // event injection will be allowed.
-    int32_t displayId = entry->displayId;
-    int32_t action = entry->action;
+    int32_t displayId = entry.displayId;
+    int32_t action = entry.action;
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
 
     // Update the touch state as needed based on the properties of the touch event.
@@ -1269,22 +1492,23 @@
 
     bool isSplit = mTempTouchState.split;
     bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 &&
-            (mTempTouchState.deviceId != entry->deviceId ||
-             mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId);
+            (mTempTouchState.deviceId != entry.deviceId || mTempTouchState.source != entry.source ||
+             mTempTouchState.displayId != displayId);
     bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
                           maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                           maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
     bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                        maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
+    const bool isFromMouse = entry.source == AINPUT_SOURCE_MOUSE;
     bool wrongDevice = false;
     if (newGesture) {
         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 "
-                  "in display %" PRId32,
-                  displayId);
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Dropping event because a pointer for a different device is already down "
+                      "in display %" PRId32,
+                      displayId);
+            }
             // TODO: test multiple simultaneous input streams.
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             switchedDevice = false;
@@ -1293,16 +1517,16 @@
         }
         mTempTouchState.reset();
         mTempTouchState.down = down;
-        mTempTouchState.deviceId = entry->deviceId;
-        mTempTouchState.source = entry->source;
+        mTempTouchState.deviceId = entry.deviceId;
+        mTempTouchState.source = entry.source;
         mTempTouchState.displayId = displayId;
         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 "
-              "in display %" PRId32,
-              displayId);
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGI("Dropping move event because a pointer for a different device is already active "
+                  "in display %" PRId32,
+                  displayId);
+        }
         // TODO: test multiple simultaneous input streams.
         injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
         switchedDevice = false;
@@ -1313,9 +1537,17 @@
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
 
+        int32_t x;
+        int32_t y;
         int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-        int32_t x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
-        int32_t y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        // Always dispatch mouse events to cursor position.
+        if (isFromMouse) {
+            x = int32_t(entry.xCursorPosition);
+            y = int32_t(entry.yCursorPosition);
+        } else {
+            x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
+            y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
+        }
         bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
         sp<InputWindowHandle> newTouchedWindowHandle =
                 findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/,
@@ -1328,8 +1560,8 @@
         // Figure out whether splitting will be allowed for this window.
         if (newTouchedWindowHandle != nullptr &&
             newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
-            // New window supports splitting.
-            isSplit = true;
+            // New window supports splitting, but we should never split mouse events.
+            isSplit = !isFromMouse;
         } else if (isSplit) {
             // New window does not support splitting but we have already split events.
             // Ignore the new window.
@@ -1372,7 +1604,7 @@
             // Update the temporary touch state.
             BitSet32 pointerIds;
             if (isSplit) {
-                uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
+                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
                 pointerIds.markBit(pointerId);
             }
             mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
@@ -1384,20 +1616,20 @@
 
         // If the pointer is not currently down, then ignore the event.
         if (!mTempTouchState.down) {
-#if DEBUG_FOCUS
-            ALOGD("Dropping event because the pointer is not down or we previously "
-                  "dropped the pointer down event in display %" PRId32,
-                  displayId);
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Dropping event because the pointer is not down or we previously "
+                      "dropped the pointer down event in display %" PRId32,
+                      displayId);
+            }
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
 
         // Check whether touches should slip outside of the current foreground window.
-        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 &&
+        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
             mTempTouchState.isSlippery()) {
-            int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
-            int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+            int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+            int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
 
             sp<InputWindowHandle> oldTouchedWindowHandle =
                     mTempTouchState.getFirstForegroundWindowHandle();
@@ -1405,11 +1637,11 @@
                     findTouchedWindowAtLocked(displayId, x, y);
             if (oldTouchedWindowHandle != newTouchedWindowHandle &&
                 oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
-                ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
-                      oldTouchedWindowHandle->getName().c_str(),
-                      newTouchedWindowHandle->getName().c_str(), displayId);
-#endif
+                if (DEBUG_FOCUS) {
+                    ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
+                          oldTouchedWindowHandle->getName().c_str(),
+                          newTouchedWindowHandle->getName().c_str(), displayId);
+                }
                 // Make a slippery exit from the old window.
                 mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
                                                   InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
@@ -1431,7 +1663,7 @@
 
                 BitSet32 pointerIds;
                 if (isSplit) {
-                    pointerIds.markBit(entry->pointerProperties[0].id);
+                    pointerIds.markBit(entry.pointerProperties[0].id);
                 }
                 mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
             }
@@ -1469,7 +1701,7 @@
         for (const TouchedWindow& touchedWindow : mTempTouchState.windows) {
             if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
                 haveForegroundWindow = true;
-                if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) {
+                if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
                     injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
                     injectionPermission = INJECTION_PERMISSION_DENIED;
                     goto Failed;
@@ -1478,11 +1710,11 @@
         }
         bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty();
         if (!haveForegroundWindow && !hasGestureMonitor) {
-#if DEBUG_FOCUS
-            ALOGD("Dropping event because there is no touched foreground window in display %" PRId32
-                  " or gesture monitor to receive it.",
-                  displayId);
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Dropping event because there is no touched foreground window in display "
+                      "%" PRId32 " or gesture monitor to receive it.",
+                      displayId);
+            }
             injectionResult = INPUT_EVENT_INJECTION_FAILED;
             goto Failed;
         }
@@ -1575,7 +1807,7 @@
 Failed:
     // Check injection permission once and for all.
     if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) {
-        if (checkInjectionPermission(nullptr, entry->injectionState)) {
+        if (checkInjectionPermission(nullptr, entry.injectionState)) {
             injectionPermission = INJECTION_PERMISSION_GRANTED;
         } else {
             injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -1586,25 +1818,26 @@
     if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
         if (!wrongDevice) {
             if (switchedDevice) {
-#if DEBUG_FOCUS
-                ALOGD("Conflicting pointer actions: Switched to a different device.");
-#endif
+                if (DEBUG_FOCUS) {
+                    ALOGD("Conflicting pointer actions: Switched to a different device.");
+                }
                 *outConflictingPointerActions = true;
             }
 
             if (isHoverAction) {
                 // Started hovering, therefore no longer down.
                 if (oldState && oldState->down) {
-#if DEBUG_FOCUS
-                    ALOGD("Conflicting pointer actions: Hover received while pointer was down.");
-#endif
+                    if (DEBUG_FOCUS) {
+                        ALOGD("Conflicting pointer actions: Hover received while pointer was "
+                              "down.");
+                    }
                     *outConflictingPointerActions = true;
                 }
                 mTempTouchState.reset();
                 if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                     maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
-                    mTempTouchState.deviceId = entry->deviceId;
-                    mTempTouchState.source = entry->source;
+                    mTempTouchState.deviceId = entry.deviceId;
+                    mTempTouchState.source = entry.source;
                     mTempTouchState.displayId = displayId;
                 }
             } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
@@ -1614,16 +1847,16 @@
             } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
                 // First pointer went down.
                 if (oldState && oldState->down) {
-#if DEBUG_FOCUS
-                    ALOGD("Conflicting pointer actions: Down received while already down.");
-#endif
+                    if (DEBUG_FOCUS) {
+                        ALOGD("Conflicting pointer actions: Down received while already down.");
+                    }
                     *outConflictingPointerActions = true;
                 }
             } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
                 // One pointer went up.
                 if (isSplit) {
                     int32_t pointerIndex = getMotionEventActionPointerIndex(action);
-                    uint32_t pointerId = entry->pointerProperties[pointerIndex].id;
+                    uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
 
                     for (size_t i = 0; i < mTempTouchState.windows.size();) {
                         TouchedWindow& touchedWindow = mTempTouchState.windows[i];
@@ -1657,9 +1890,9 @@
             mLastHoverWindowHandle = newHoverWindowHandle;
         }
     } else {
-#if DEBUG_FOCUS
-        ALOGD("Not updating touch focus because injection was denied.");
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGD("Not updating touch focus because injection was denied.");
+        }
     }
 
 Unresponsive:
@@ -1668,34 +1901,45 @@
 
     nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime);
     updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication);
-#if DEBUG_FOCUS
-    ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
-          "timeSpentWaitingForApplication=%0.1fms",
-          injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, "
+              "timeSpentWaitingForApplication=%0.1fms",
+              injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0);
+    }
     return injectionResult;
 }
 
 void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
                                             int32_t targetFlags, BitSet32 pointerIds,
                                             std::vector<InputTarget>& inputTargets) {
-    sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
-    if (inputChannel == nullptr) {
-        ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
-        return;
-    }
+    std::vector<InputTarget>::iterator it =
+            std::find_if(inputTargets.begin(), inputTargets.end(),
+                         [&windowHandle](const InputTarget& inputTarget) {
+                             return inputTarget.inputChannel->getConnectionToken() ==
+                                     windowHandle->getToken();
+                         });
 
     const InputWindowInfo* windowInfo = windowHandle->getInfo();
-    InputTarget target;
-    target.inputChannel = inputChannel;
-    target.flags = targetFlags;
-    target.xOffset = -windowInfo->frameLeft;
-    target.yOffset = -windowInfo->frameTop;
-    target.globalScaleFactor = windowInfo->globalScaleFactor;
-    target.windowXScale = windowInfo->windowXScale;
-    target.windowYScale = windowInfo->windowYScale;
-    target.pointerIds = pointerIds;
-    inputTargets.push_back(target);
+
+    if (it == inputTargets.end()) {
+        InputTarget inputTarget;
+        sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+        if (inputChannel == nullptr) {
+            ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+            return;
+        }
+        inputTarget.inputChannel = inputChannel;
+        inputTarget.flags = targetFlags;
+        inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+        inputTargets.push_back(inputTarget);
+        it = inputTargets.end() - 1;
+    }
+
+    ALOG_ASSERT(it->flags == targetFlags);
+    ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+
+    it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
+                    windowInfo->windowXScale, windowInfo->windowYScale);
 }
 
 void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1718,10 +1962,7 @@
     InputTarget target;
     target.inputChannel = monitor.inputChannel;
     target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-    target.xOffset = xOffset;
-    target.yOffset = yOffset;
-    target.pointerIds.clear();
-    target.globalScaleFactor = 1.0f;
+    target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
     inputTargets.push_back(target);
 }
 
@@ -1783,25 +2024,22 @@
 
 std::string InputDispatcher::checkWindowReadyForMoreInputLocked(
         nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle,
-        const EventEntry* eventEntry, const char* targetType) {
+        const EventEntry& eventEntry, const char* targetType) {
     // If the window is paused then keep waiting.
     if (windowHandle->getInfo()->paused) {
         return StringPrintf("Waiting because the %s window is paused.", targetType);
     }
 
     // If the window's connection is not registered then keep waiting.
-    ssize_t connectionIndex =
-            getConnectionIndexLocked(getInputChannelLocked(windowHandle->getToken()));
-    if (connectionIndex < 0) {
+    sp<Connection> connection = getConnectionLocked(windowHandle->getToken());
+    if (connection == nullptr) {
         return StringPrintf("Waiting because the %s window's input channel is not "
                             "registered with the input dispatcher.  The window may be in the "
-                            "process "
-                            "of being removed.",
+                            "process of being removed.",
                             targetType);
     }
 
     // If the connection is dead then keep waiting.
-    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
     if (connection->status != Connection::STATUS_NORMAL) {
         return StringPrintf("Waiting because the %s window's input connection is %s."
                             "The window may be in the process of being removed.",
@@ -1811,13 +2049,13 @@
     // If the connection is backed up then keep waiting.
     if (connection->inputPublisherBlocked) {
         return StringPrintf("Waiting because the %s window's input channel is full.  "
-                            "Outbound queue length: %d.  Wait queue length: %d.",
-                            targetType, connection->outboundQueue.count(),
-                            connection->waitQueue.count());
+                            "Outbound queue length: %zu.  Wait queue length: %zu.",
+                            targetType, connection->outboundQueue.size(),
+                            connection->waitQueue.size());
     }
 
     // Ensure that the dispatch queues aren't too far backed up for this event.
-    if (eventEntry->type == EventEntry::TYPE_KEY) {
+    if (eventEntry.type == EventEntry::Type::KEY) {
         // If the event is a key event, then we must wait for all previous events to
         // complete before delivering it because previous events may have the
         // side-effect of transferring focus to a different window and we want to
@@ -1829,13 +2067,13 @@
         // often anticipate pending UI changes when typing on a keyboard.
         // To obtain this behavior, we must serialize key events with respect to all
         // prior input events.
-        if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
+        if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) {
             return StringPrintf("Waiting to send key event because the %s window has not "
                                 "finished processing all of the input events that were previously "
-                                "delivered to it.  Outbound queue length: %d.  Wait queue length: "
-                                "%d.",
-                                targetType, connection->outboundQueue.count(),
-                                connection->waitQueue.count());
+                                "delivered to it.  Outbound queue length: %zu.  Wait queue length: "
+                                "%zu.",
+                                targetType, connection->outboundQueue.size(),
+                                connection->waitQueue.size());
         }
     } else {
         // Touch events can always be sent to a window immediately because the user intended
@@ -1853,16 +2091,17 @@
         // The one case where we pause input event delivery is when the wait queue is piling
         // up with lots of events because the application is not responding.
         // This condition ensures that ANRs are detected reliably.
-        if (!connection->waitQueue.isEmpty() &&
-            currentTime >= connection->waitQueue.head->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) {
+        if (!connection->waitQueue.empty() &&
+            currentTime >=
+                    connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) {
             return StringPrintf("Waiting to send non-key event because the %s window has not "
                                 "finished processing certain input events that were delivered to "
                                 "it over "
-                                "%0.1fms ago.  Wait queue length: %d.  Wait queue head age: "
+                                "%0.1fms ago.  Wait queue length: %zu.  Wait queue head age: "
                                 "%0.1fms.",
                                 targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
-                                connection->waitQueue.count(),
-                                (currentTime - connection->waitQueue.head->deliveryTime) *
+                                connection->waitQueue.size(),
+                                (currentTime - connection->waitQueue.front()->deliveryTime) *
                                         0.000001f);
         }
     }
@@ -1888,7 +2127,11 @@
     }
 }
 
-void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
+void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
+    if (eventEntry.type == EventEntry::Type::FOCUS) {
+        // Focus events are passed to apps, but do not represent user activity.
+        return;
+    }
     int32_t displayId = getTargetDisplayId(eventEntry);
     sp<InputWindowHandle> focusedWindowHandle =
             getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
@@ -1903,38 +2146,46 @@
     }
 
     int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
-    switch (eventEntry->type) {
-        case EventEntry::TYPE_MOTION: {
-            const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry);
-            if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) {
+    switch (eventEntry.type) {
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
+            if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
                 return;
             }
 
-            if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) {
+            if (MotionEvent::isTouchEvent(motionEntry.source, motionEntry.action)) {
                 eventType = USER_ACTIVITY_EVENT_TOUCH;
             }
             break;
         }
-        case EventEntry::TYPE_KEY: {
-            const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
-            if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
+        case EventEntry::Type::KEY: {
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
+            if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
                 return;
             }
             eventType = USER_ACTIVITY_EVENT_BUTTON;
             break;
         }
+        case EventEntry::Type::FOCUS:
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            LOG_ALWAYS_FATAL("%s events are not user activity",
+                             EventEntry::typeToString(eventEntry.type));
+            break;
+        }
     }
 
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doPokeUserActivityLockedInterruptible);
-    commandEntry->eventTime = eventEntry->eventTime;
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible);
+    commandEntry->eventTime = eventEntry.eventTime;
     commandEntry->userActivityEventType = eventType;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
                                                  const sp<Connection>& connection,
                                                  EventEntry* eventEntry,
-                                                 const InputTarget* inputTarget) {
+                                                 const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
         std::string message =
                 StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
@@ -1943,11 +2194,10 @@
     }
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
-          "xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
-          "windowScaleFactor=(%f, %f), pointerIds=0x%x",
-          connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset,
-          inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale,
-          inputTarget->windowYScale, inputTarget->pointerIds.value);
+          "globalScaleFactor=%f, pointerIds=0x%x %s",
+          connection->getInputChannelName().c_str(), inputTarget.flags,
+          inputTarget.globalScaleFactor, inputTarget.pointerIds.value,
+          inputTarget.getPointerInfoString().c_str());
 #endif
 
     // Skip this event if the connection status is not normal.
@@ -1961,20 +2211,23 @@
     }
 
     // Split a motion event if needed.
-    if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
-        ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
+    if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
+        LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION,
+                            "Entry type %s should not have FLAG_SPLIT",
+                            EventEntry::typeToString(eventEntry->type));
 
-        MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
-        if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
+        const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
+        if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
             MotionEntry* splitMotionEntry =
-                    splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
+                    splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
             if (!splitMotionEntry) {
                 return; // split event was dropped
             }
-#if DEBUG_FOCUS
-            ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str());
-            logOutboundMotionDetails("  ", splitMotionEntry);
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("channel '%s' ~ Split motion event.",
+                      connection->getInputChannelName().c_str());
+                logOutboundMotionDetails("  ", *splitMotionEntry);
+            }
             enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget);
             splitMotionEntry->release();
             return;
@@ -1988,7 +2241,7 @@
 void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
                                                    const sp<Connection>& connection,
                                                    EventEntry* eventEntry,
-                                                   const InputTarget* inputTarget) {
+                                                   const InputTarget& inputTarget) {
     if (ATRACE_ENABLED()) {
         std::string message =
                 StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32
@@ -1997,7 +2250,7 @@
         ATRACE_NAME(message.c_str());
     }
 
-    bool wasEmpty = connection->outboundQueue.isEmpty();
+    bool wasEmpty = connection->outboundQueue.empty();
 
     // Enqueue dispatch entries for the requested modes.
     enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
@@ -2014,14 +2267,14 @@
                                InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
-    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
+    if (wasEmpty && !connection->outboundQueue.empty()) {
         startDispatchCycleLocked(currentTime, connection);
     }
 }
 
 void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
                                                  EventEntry* eventEntry,
-                                                 const InputTarget* inputTarget,
+                                                 const InputTarget& inputTarget,
                                                  int32_t dispatchMode) {
     if (ATRACE_ENABLED()) {
         std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
@@ -2029,7 +2282,7 @@
                                            dispatchModeToString(dispatchMode).c_str());
         ATRACE_NAME(message.c_str());
     }
-    int32_t inputTargetFlags = inputTarget->flags;
+    int32_t inputTargetFlags = inputTarget.flags;
     if (!(inputTargetFlags & dispatchMode)) {
         return;
     }
@@ -2037,18 +2290,18 @@
 
     // This is a new event.
     // 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->globalScaleFactor, inputTarget->windowXScale,
-                              inputTarget->windowYScale);
+    std::unique_ptr<DispatchEntry> dispatchEntry =
+            createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
 
+    // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
+    // different EventEntry than what was passed in.
+    EventEntry* newEntry = dispatchEntry->eventEntry;
     // Apply target flags and update the connection's input state.
-    switch (eventEntry->type) {
-        case EventEntry::TYPE_KEY: {
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
-            dispatchEntry->resolvedAction = keyEntry->action;
-            dispatchEntry->resolvedFlags = keyEntry->flags;
+    switch (newEntry->type) {
+        case EventEntry::Type::KEY: {
+            const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry);
+            dispatchEntry->resolvedAction = keyEntry.action;
+            dispatchEntry->resolvedFlags = keyEntry.flags;
 
             if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction,
                                                  dispatchEntry->resolvedFlags)) {
@@ -2056,14 +2309,13 @@
                 ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
                       connection->getInputChannelName().c_str());
 #endif
-                delete dispatchEntry;
                 return; // skip the inconsistent event
             }
             break;
         }
 
-        case EventEntry::TYPE_MOTION: {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+        case EventEntry::Type::MOTION: {
+            const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry);
             if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
             } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
@@ -2075,11 +2327,11 @@
             } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
             } else {
-                dispatchEntry->resolvedAction = motionEntry->action;
+                dispatchEntry->resolvedAction = motionEntry.action;
             }
             if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE &&
-                !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source,
-                                                   motionEntry->displayId)) {
+                !connection->inputState.isHovering(motionEntry.deviceId, motionEntry.source,
+                                                   motionEntry.displayId)) {
 #if DEBUG_DISPATCH_CYCLE
                 ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter "
                       "event",
@@ -2088,7 +2340,7 @@
                 dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
             }
 
-            dispatchEntry->resolvedFlags = motionEntry->flags;
+            dispatchEntry->resolvedFlags = motionEntry.flags;
             if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
                 dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
             }
@@ -2103,24 +2355,32 @@
                       "event",
                       connection->getInputChannelName().c_str());
 #endif
-                delete dispatchEntry;
                 return; // skip the inconsistent event
             }
 
-            dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction,
-                                            inputTarget->inputChannel->getToken());
+            dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
+                                            inputTarget.inputChannel->getConnectionToken());
 
             break;
         }
+        case EventEntry::Type::FOCUS: {
+            break;
+        }
+        case EventEntry::Type::CONFIGURATION_CHANGED:
+        case EventEntry::Type::DEVICE_RESET: {
+            LOG_ALWAYS_FATAL("%s events should not go to apps",
+                             EventEntry::typeToString(newEntry->type));
+            break;
+        }
     }
 
     // Remember that we are waiting for this dispatch to complete.
     if (dispatchEntry->hasForegroundTarget()) {
-        incrementPendingForegroundDispatches(eventEntry);
+        incrementPendingForegroundDispatches(newEntry);
     }
 
     // Enqueue the dispatch entry.
-    connection->outboundQueue.enqueueAtTail(dispatchEntry);
+    connection->outboundQueue.push_back(dispatchEntry.release());
     traceOutboundQueueLength(connection);
 }
 
@@ -2146,9 +2406,10 @@
         return;
     }
 
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible);
     commandEntry->newToken = newToken;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
@@ -2162,23 +2423,22 @@
     ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
 #endif
 
-    while (connection->status == Connection::STATUS_NORMAL &&
-           !connection->outboundQueue.isEmpty()) {
-        DispatchEntry* dispatchEntry = connection->outboundQueue.head;
+    while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
+        DispatchEntry* dispatchEntry = connection->outboundQueue.front();
         dispatchEntry->deliveryTime = currentTime;
 
         // Publish the event.
         status_t status;
         EventEntry* eventEntry = dispatchEntry->eventEntry;
         switch (eventEntry->type) {
-            case EventEntry::TYPE_KEY: {
+            case EventEntry::Type::KEY: {
                 KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
 
                 // Publish the key event.
                 status = connection->inputPublisher
                                  .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId,
                                                   keyEntry->source, keyEntry->displayId,
-                                                  dispatchEntry->resolvedAction,
+                                                  INVALID_HMAC, dispatchEntry->resolvedAction,
                                                   dispatchEntry->resolvedFlags, keyEntry->keyCode,
                                                   keyEntry->scanCode, keyEntry->metaState,
                                                   keyEntry->repeatCount, keyEntry->downTime,
@@ -2186,32 +2446,34 @@
                 break;
             }
 
-            case EventEntry::TYPE_MOTION: {
+            case EventEntry::Type::MOTION: {
                 MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
                 PointerCoords scaledCoords[MAX_POINTERS];
                 const PointerCoords* usingCoords = motionEntry->pointerCoords;
 
-                // Set the X and Y offset depending on the input source.
-                float xOffset, yOffset;
+                // Set the X and Y offset and X and Y scale depending on the input source.
+                float xOffset = 0.0f, yOffset = 0.0f;
+                float xScale = 1.0f, yScale = 1.0f;
                 if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) &&
                     !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
                     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) {
+                    xScale = dispatchEntry->windowXScale;
+                    yScale = dispatchEntry->windowYScale;
+                    xOffset = dispatchEntry->xOffset * xScale;
+                    yOffset = dispatchEntry->yOffset * yScale;
+                    if (globalScaleFactor != 1.0f) {
                         for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
                             scaledCoords[i] = motionEntry->pointerCoords[i];
-                            scaledCoords[i].scale(globalScaleFactor, wxs, wys);
+                            // Don't apply window scale here since we don't want scale to affect raw
+                            // coordinates. The scale will be sent back to the client and applied
+                            // later when requesting relative coordinates.
+                            scaledCoords[i].scale(globalScaleFactor, 1 /* windowXScale */,
+                                                  1 /* windowYScale */);
                         }
                         usingCoords = scaledCoords;
                     }
                 } else {
-                    xOffset = 0.0f;
-                    yOffset = 0.0f;
-
                     // We don't want the dispatch target to know.
                     if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
                         for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {
@@ -2225,29 +2487,42 @@
                 status = connection->inputPublisher
                                  .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId,
                                                      motionEntry->source, motionEntry->displayId,
-                                                     dispatchEntry->resolvedAction,
+                                                     INVALID_HMAC, dispatchEntry->resolvedAction,
                                                      motionEntry->actionButton,
                                                      dispatchEntry->resolvedFlags,
                                                      motionEntry->edgeFlags, motionEntry->metaState,
                                                      motionEntry->buttonState,
-                                                     motionEntry->classification, xOffset, yOffset,
-                                                     motionEntry->xPrecision,
-                                                     motionEntry->yPrecision, motionEntry->downTime,
-                                                     motionEntry->eventTime,
+                                                     motionEntry->classification, xScale, yScale,
+                                                     xOffset, yOffset, motionEntry->xPrecision,
+                                                     motionEntry->yPrecision,
+                                                     motionEntry->xCursorPosition,
+                                                     motionEntry->yCursorPosition,
+                                                     motionEntry->downTime, motionEntry->eventTime,
                                                      motionEntry->pointerCount,
                                                      motionEntry->pointerProperties, usingCoords);
+                reportTouchEventForStatistics(*motionEntry);
+                break;
+            }
+            case EventEntry::Type::FOCUS: {
+                FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry);
+                status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
+                                                                      focusEntry->hasFocus,
+                                                                      mInTouchMode);
                 break;
             }
 
-            default:
-                ALOG_ASSERT(false);
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
+                                 EventEntry::typeToString(eventEntry->type));
                 return;
+            }
         }
 
         // Check the result.
         if (status) {
             if (status == WOULD_BLOCK) {
-                if (connection->waitQueue.isEmpty()) {
+                if (connection->waitQueue.empty()) {
                     ALOGE("channel '%s' ~ Could not publish event because the pipe is full. "
                           "This is unexpected because the wait queue is empty, so the pipe "
                           "should be empty and we shouldn't have any problems writing an "
@@ -2274,9 +2549,11 @@
         }
 
         // Re-enqueue the event on the wait queue.
-        connection->outboundQueue.dequeue(dispatchEntry);
+        connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(),
+                                                    connection->outboundQueue.end(),
+                                                    dispatchEntry));
         traceOutboundQueueLength(connection);
-        connection->waitQueue.enqueueAtTail(dispatchEntry);
+        connection->waitQueue.push_back(dispatchEntry);
         traceWaitQueueLength(connection);
     }
 }
@@ -2309,9 +2586,9 @@
 #endif
 
     // Clear the dispatch queues.
-    drainDispatchQueue(&connection->outboundQueue);
+    drainDispatchQueue(connection->outboundQueue);
     traceOutboundQueueLength(connection);
-    drainDispatchQueue(&connection->waitQueue);
+    drainDispatchQueue(connection->waitQueue);
     traceWaitQueueLength(connection);
 
     // The connection appears to be unrecoverably broken.
@@ -2326,9 +2603,10 @@
     }
 }
 
-void InputDispatcher::drainDispatchQueue(Queue<DispatchEntry>* queue) {
-    while (!queue->isEmpty()) {
-        DispatchEntry* dispatchEntry = queue->dequeueAtHead();
+void InputDispatcher::drainDispatchQueue(std::deque<DispatchEntry*>& queue) {
+    while (!queue.empty()) {
+        DispatchEntry* dispatchEntry = queue.front();
+        queue.pop_front();
         releaseDispatchEntry(dispatchEntry);
     }
 }
@@ -2346,8 +2624,7 @@
     { // acquire lock
         std::scoped_lock _l(d->mLock);
 
-        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
-        if (connectionIndex < 0) {
+        if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) {
             ALOGE("Received spurious receive callback for unknown input channel.  "
                   "fd=%d, events=0x%x",
                   fd, events);
@@ -2355,7 +2632,7 @@
         }
 
         bool notify;
-        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
+        sp<Connection> connection = d->mConnectionsByFd[fd];
         if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
             if (!(events & ALOOPER_EVENT_INPUT)) {
                 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
@@ -2409,8 +2686,8 @@
 
 void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
         const CancelationOptions& options) {
-    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-        synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(i), options);
+    for (const auto& pair : mConnectionsByFd) {
+        synthesizeCancelationEventsForConnectionLocked(pair.second, options);
     }
 }
 
@@ -2433,10 +2710,12 @@
 
 void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
         const sp<InputChannel>& channel, const CancelationOptions& options) {
-    ssize_t index = getConnectionIndexLocked(channel);
-    if (index >= 0) {
-        synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(index), options);
+    sp<Connection> connection = getConnectionLocked(channel->getConnectionToken());
+    if (connection == nullptr) {
+        return;
     }
+
+    synthesizeCancelationEventsForConnectionLocked(connection, options);
 }
 
 void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
@@ -2447,58 +2726,125 @@
 
     nsecs_t currentTime = now();
 
-    std::vector<EventEntry*> cancelationEvents;
-    connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options);
+    std::vector<EventEntry*> cancelationEvents =
+            connection->inputState.synthesizeCancelationEvents(currentTime, options);
 
-    if (!cancelationEvents.empty()) {
+    if (cancelationEvents.empty()) {
+        return;
+    }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
-        ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
-              "with reality: %s, mode=%d.",
-              connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
-              options.mode);
+    ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync "
+          "with reality: %s, mode=%d.",
+          connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason,
+          options.mode);
 #endif
-        for (size_t i = 0; i < cancelationEvents.size(); i++) {
-            EventEntry* cancelationEventEntry = cancelationEvents[i];
-            switch (cancelationEventEntry->type) {
-                case EventEntry::TYPE_KEY:
-                    logOutboundKeyDetails("cancel - ",
-                                          static_cast<KeyEntry*>(cancelationEventEntry));
-                    break;
-                case EventEntry::TYPE_MOTION:
-                    logOutboundMotionDetails("cancel - ",
-                                             static_cast<MotionEntry*>(cancelationEventEntry));
-                    break;
+
+    InputTarget target;
+    sp<InputWindowHandle> windowHandle =
+            getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+    if (windowHandle != nullptr) {
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+                                     windowInfo->windowXScale, windowInfo->windowYScale);
+        target.globalScaleFactor = windowInfo->globalScaleFactor;
+    }
+    target.inputChannel = connection->inputChannel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+    for (size_t i = 0; i < cancelationEvents.size(); i++) {
+        EventEntry* cancelationEventEntry = cancelationEvents[i];
+        switch (cancelationEventEntry->type) {
+            case EventEntry::Type::KEY: {
+                logOutboundKeyDetails("cancel - ",
+                                      static_cast<const KeyEntry&>(*cancelationEventEntry));
+                break;
             }
-
-            InputTarget target;
-            sp<InputWindowHandle> windowHandle =
-                    getWindowHandleLocked(connection->inputChannel->getToken());
-            if (windowHandle != nullptr) {
-                const InputWindowInfo* windowInfo = windowHandle->getInfo();
-                target.xOffset = -windowInfo->frameLeft;
-                target.yOffset = -windowInfo->frameTop;
-                target.globalScaleFactor = windowInfo->globalScaleFactor;
-                target.windowXScale = windowInfo->windowXScale;
-                target.windowYScale = windowInfo->windowYScale;
-            } else {
-                target.xOffset = 0;
-                target.yOffset = 0;
-                target.globalScaleFactor = 1.0f;
+            case EventEntry::Type::MOTION: {
+                logOutboundMotionDetails("cancel - ",
+                                         static_cast<const MotionEntry&>(*cancelationEventEntry));
+                break;
             }
-            target.inputChannel = connection->inputChannel;
-            target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
-
-            enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
-                                       &target, InputTarget::FLAG_DISPATCH_AS_IS);
-
-            cancelationEventEntry->release();
+            case EventEntry::Type::FOCUS: {
+                LOG_ALWAYS_FATAL("Canceling focus events is not supported");
+                break;
+            }
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+                                 EventEntry::typeToString(cancelationEventEntry->type));
+                break;
+            }
         }
 
-        startDispatchCycleLocked(currentTime, connection);
+        enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
+
+        cancelationEventEntry->release();
     }
+
+    startDispatchCycleLocked(currentTime, connection);
 }
 
-MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry,
+void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
+        const sp<Connection>& connection) {
+    if (connection->status == Connection::STATUS_BROKEN) {
+        return;
+    }
+
+    nsecs_t currentTime = now();
+
+    std::vector<EventEntry*> downEvents =
+            connection->inputState.synthesizePointerDownEvents(currentTime);
+
+    if (downEvents.empty()) {
+        return;
+    }
+
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+        ALOGD("channel '%s' ~ Synthesized %zu down events to ensure consistent event stream.",
+              connection->getInputChannelName().c_str(), downEvents.size());
+#endif
+
+    InputTarget target;
+    sp<InputWindowHandle> windowHandle =
+            getWindowHandleLocked(connection->inputChannel->getConnectionToken());
+    if (windowHandle != nullptr) {
+        const InputWindowInfo* windowInfo = windowHandle->getInfo();
+        target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+                                     windowInfo->windowXScale, windowInfo->windowYScale);
+        target.globalScaleFactor = windowInfo->globalScaleFactor;
+    }
+    target.inputChannel = connection->inputChannel;
+    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+    for (EventEntry* downEventEntry : downEvents) {
+        switch (downEventEntry->type) {
+            case EventEntry::Type::MOTION: {
+                logOutboundMotionDetails("down - ",
+                        static_cast<const MotionEntry&>(*downEventEntry));
+                break;
+            }
+
+            case EventEntry::Type::KEY:
+            case EventEntry::Type::FOCUS:
+            case EventEntry::Type::CONFIGURATION_CHANGED:
+            case EventEntry::Type::DEVICE_RESET: {
+                LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+                                     EventEntry::typeToString(downEventEntry->type));
+                break;
+            }
+        }
+
+        enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref
+                                   target, InputTarget::FLAG_DISPATCH_AS_IS);
+
+        downEventEntry->release();
+    }
+
+    startDispatchCycleLocked(currentTime, connection);
+}
+
+MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry,
                                                BitSet32 pointerIds) {
     ALOG_ASSERT(pointerIds.value != 0);
 
@@ -2506,19 +2852,19 @@
     PointerProperties splitPointerProperties[MAX_POINTERS];
     PointerCoords splitPointerCoords[MAX_POINTERS];
 
-    uint32_t originalPointerCount = originalMotionEntry->pointerCount;
+    uint32_t originalPointerCount = originalMotionEntry.pointerCount;
     uint32_t splitPointerCount = 0;
 
     for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
          originalPointerIndex++) {
         const PointerProperties& pointerProperties =
-                originalMotionEntry->pointerProperties[originalPointerIndex];
+                originalMotionEntry.pointerProperties[originalPointerIndex];
         uint32_t pointerId = uint32_t(pointerProperties.id);
         if (pointerIds.hasBit(pointerId)) {
             splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
             splitPointerProperties[splitPointerCount].copyFrom(pointerProperties);
             splitPointerCoords[splitPointerCount].copyFrom(
-                    originalMotionEntry->pointerCoords[originalPointerIndex]);
+                    originalMotionEntry.pointerCoords[originalPointerIndex]);
             splitPointerCount += 1;
         }
     }
@@ -2536,13 +2882,13 @@
         return nullptr;
     }
 
-    int32_t action = originalMotionEntry->action;
+    int32_t action = originalMotionEntry.action;
     int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
     if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
         maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
         int32_t originalPointerIndex = getMotionEventActionPointerIndex(action);
         const PointerProperties& pointerProperties =
-                originalMotionEntry->pointerProperties[originalPointerIndex];
+                originalMotionEntry.pointerProperties[originalPointerIndex];
         uint32_t pointerId = uint32_t(pointerProperties.id);
         if (pointerIds.hasBit(pointerId)) {
             if (pointerIds.count() == 1) {
@@ -2566,18 +2912,19 @@
     }
 
     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, splitPointerCount,
-                            splitPointerProperties, splitPointerCoords, 0, 0);
+            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.xCursorPosition,
+                            originalMotionEntry.yCursorPosition, originalMotionEntry.downTime,
+                            splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0);
 
-    if (originalMotionEntry->injectionState) {
-        splitMotionEntry->injectionState = originalMotionEntry->injectionState;
+    if (originalMotionEntry.injectionState) {
+        splitMotionEntry->injectionState = originalMotionEntry.injectionState;
         splitMotionEntry->injectionState->refCount += 1;
     }
 
@@ -2673,8 +3020,9 @@
     accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState);
 
     KeyEvent event;
-    event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode,
-                     args->scanCode, metaState, repeatCount, args->downTime, args->eventTime);
+    event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action,
+                     flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime,
+                     args->eventTime);
 
     android::base::Timer t;
     mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
@@ -2720,11 +3068,13 @@
 #if DEBUG_INBOUND_EVENT_DETAILS
     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,
+          "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
+          "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
+          "yCursorPosition=%f, downTime=%" PRId64,
           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);
+          args->edgeFlags, args->xPrecision, args->yPrecision, args->xCursorPosition,
+          args->yCursorPosition, args->downTime);
     for (uint32_t i = 0; i < args->pointerCount; i++) {
         ALOGD("  Pointer %d: id=%d, toolType=%d, "
               "x=%f, y=%f, pressure=%f, size=%f, "
@@ -2765,10 +3115,12 @@
             mLock.unlock();
 
             MotionEvent event;
-            event.initialize(args->deviceId, args->source, args->displayId, args->action,
-                             args->actionButton, args->flags, args->edgeFlags, args->metaState,
-                             args->buttonState, args->classification, 0, 0, args->xPrecision,
-                             args->yPrecision, args->downTime, args->eventTime, args->pointerCount,
+            event.initialize(args->deviceId, args->source, args->displayId, INVALID_HMAC,
+                             args->action, args->actionButton, args->flags, args->edgeFlags,
+                             args->metaState, args->buttonState, args->classification, 1 /*xScale*/,
+                             1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision,
+                             args->yPrecision, args->xCursorPosition, args->yCursorPosition,
+                             args->downTime, args->eventTime, args->pointerCount,
                              args->pointerProperties, args->pointerCoords);
 
             policyFlags |= POLICY_FLAG_FILTERED;
@@ -2785,8 +3137,9 @@
                                 args->displayId, policyFlags, args->action, args->actionButton,
                                 args->flags, args->metaState, args->buttonState,
                                 args->classification, args->edgeFlags, args->xPrecision,
-                                args->yPrecision, args->downTime, args->pointerCount,
-                                args->pointerProperties, args->pointerCoords, 0, 0);
+                                args->yPrecision, args->xCursorPosition, args->yCursorPosition,
+                                args->downTime, args->pointerCount, args->pointerProperties,
+                                args->pointerCoords, 0, 0);
 
         needWake = enqueueInboundEventLocked(newEntry);
         mLock.unlock();
@@ -2849,26 +3202,25 @@
         policyFlags |= POLICY_FLAG_TRUSTED;
     }
 
-    EventEntry* firstInjectedEntry;
-    EventEntry* lastInjectedEntry;
+    std::queue<EventEntry*> injectedEntries;
     switch (event->getType()) {
         case AINPUT_EVENT_TYPE_KEY: {
-            KeyEvent keyEvent;
-            keyEvent.initialize(*static_cast<const KeyEvent*>(event));
-            int32_t action = keyEvent.getAction();
+            const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
+            int32_t action = incomingKey.getAction();
             if (!validateKeyEvent(action)) {
                 return INPUT_EVENT_INJECTION_FAILED;
             }
 
-            int32_t flags = keyEvent.getFlags();
-            int32_t keyCode = keyEvent.getKeyCode();
-            int32_t metaState = keyEvent.getMetaState();
-            accelerateMetaShortcuts(keyEvent.getDeviceId(), action,
+            int32_t flags = incomingKey.getFlags();
+            int32_t keyCode = incomingKey.getKeyCode();
+            int32_t metaState = incomingKey.getMetaState();
+            accelerateMetaShortcuts(VIRTUAL_KEYBOARD_ID, 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());
+            KeyEvent keyEvent;
+            keyEvent.initialize(VIRTUAL_KEYBOARD_ID, incomingKey.getSource(),
+                                incomingKey.getDisplayId(), INVALID_HMAC, action, flags, keyCode,
+                                incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(),
+                                incomingKey.getDownTime(), incomingKey.getEventTime());
 
             if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) {
                 policyFlags |= POLICY_FLAG_VIRTUAL;
@@ -2884,13 +3236,13 @@
             }
 
             mLock.lock();
-            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());
-            lastInjectedEntry = firstInjectedEntry;
+            KeyEntry* injectedEntry =
+                    new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(),
+                                 VIRTUAL_KEYBOARD_ID, keyEvent.getSource(), keyEvent.getDisplayId(),
+                                 policyFlags, action, flags, keyEvent.getKeyCode(),
+                                 keyEvent.getScanCode(), keyEvent.getMetaState(),
+                                 keyEvent.getRepeatCount(), keyEvent.getDownTime());
+            injectedEntries.push(injectedEntry);
             break;
         }
 
@@ -2918,40 +3270,44 @@
             mLock.lock();
             const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes();
             const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords();
-            firstInjectedEntry =
+            MotionEntry* injectedEntry =
                     new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
-                                    motionEvent->getDeviceId(), motionEvent->getSource(),
+                                    VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
                                     motionEvent->getDisplayId(), policyFlags, action, actionButton,
                                     motionEvent->getFlags(), motionEvent->getMetaState(),
                                     motionEvent->getButtonState(), motionEvent->getClassification(),
                                     motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
-                                    motionEvent->getYPrecision(), motionEvent->getDownTime(),
-                                    uint32_t(pointerCount), pointerProperties, samplePointerCoords,
+                                    motionEvent->getYPrecision(),
+                                    motionEvent->getRawXCursorPosition(),
+                                    motionEvent->getRawYCursorPosition(),
+                                    motionEvent->getDownTime(), uint32_t(pointerCount),
+                                    pointerProperties, samplePointerCoords,
                                     motionEvent->getXOffset(), motionEvent->getYOffset());
-            lastInjectedEntry = firstInjectedEntry;
+            injectedEntries.push(injectedEntry);
             for (size_t i = motionEvent->getHistorySize(); i > 0; i--) {
                 sampleEventTimes += 1;
                 samplePointerCoords += pointerCount;
                 MotionEntry* nextInjectedEntry =
                         new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes,
-                                        motionEvent->getDeviceId(), motionEvent->getSource(),
+                                        VIRTUAL_KEYBOARD_ID, motionEvent->getSource(),
                                         motionEvent->getDisplayId(), policyFlags, action,
                                         actionButton, motionEvent->getFlags(),
                                         motionEvent->getMetaState(), motionEvent->getButtonState(),
                                         motionEvent->getClassification(),
                                         motionEvent->getEdgeFlags(), motionEvent->getXPrecision(),
-                                        motionEvent->getYPrecision(), motionEvent->getDownTime(),
-                                        uint32_t(pointerCount), pointerProperties,
-                                        samplePointerCoords, motionEvent->getXOffset(),
-                                        motionEvent->getYOffset());
-                lastInjectedEntry->next = nextInjectedEntry;
-                lastInjectedEntry = nextInjectedEntry;
+                                        motionEvent->getYPrecision(),
+                                        motionEvent->getRawXCursorPosition(),
+                                        motionEvent->getRawYCursorPosition(),
+                                        motionEvent->getDownTime(), uint32_t(pointerCount),
+                                        pointerProperties, samplePointerCoords,
+                                        motionEvent->getXOffset(), motionEvent->getYOffset());
+                injectedEntries.push(nextInjectedEntry);
             }
             break;
         }
 
         default:
-            ALOGW("Cannot inject event of type %d", event->getType());
+            ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
             return INPUT_EVENT_INJECTION_FAILED;
     }
 
@@ -2961,13 +3317,12 @@
     }
 
     injectionState->refCount += 1;
-    lastInjectedEntry->injectionState = injectionState;
+    injectedEntries.back()->injectionState = injectionState;
 
     bool needWake = false;
-    for (EventEntry* entry = firstInjectedEntry; entry != nullptr;) {
-        EventEntry* nextEntry = entry->next;
-        needWake |= enqueueInboundEventLocked(entry);
-        entry = nextEntry;
+    while (!injectedEntries.empty()) {
+        needWake |= enqueueInboundEventLocked(injectedEntries.front());
+        injectedEntries.pop();
     }
 
     mLock.unlock();
@@ -3036,6 +3391,10 @@
     return injectionResult;
 }
 
+std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const InputEvent& event) {
+    return nullptr;
+}
+
 bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) {
     return injectorUid == 0 ||
             mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
@@ -3093,14 +3452,7 @@
 
 std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(
         int32_t displayId) const {
-    std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>::const_iterator it =
-            mWindowHandlesByDisplay.find(displayId);
-    if (it != mWindowHandlesByDisplay.end()) {
-        return it->second;
-    }
-
-    // Return an empty one if nothing found.
-    return std::vector<sp<InputWindowHandle>>();
+    return getValueByKey(mWindowHandlesByDisplay, displayId);
 }
 
 sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(
@@ -3142,6 +3494,63 @@
     return mInputChannelsByToken.at(token);
 }
 
+void InputDispatcher::updateWindowHandlesForDisplayLocked(
+        const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) {
+    if (inputWindowHandles.empty()) {
+        // Remove all handles on a display if there are no windows left.
+        mWindowHandlesByDisplay.erase(displayId);
+        return;
+    }
+
+    // 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 std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId);
+    std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById;
+    for (const sp<InputWindowHandle>& handle : oldHandles) {
+        oldHandlesById[handle->getId()] = handle;
+    }
+
+    std::vector<sp<InputWindowHandle>> newHandles;
+    for (const sp<InputWindowHandle>& handle : inputWindowHandles) {
+        if (!handle->updateInfo()) {
+            // handle no longer valid
+            continue;
+        }
+
+        const InputWindowInfo* info = handle->getInfo();
+        if ((getInputChannelLocked(handle->getToken()) == nullptr &&
+             info->portalToDisplayId == ADISPLAY_ID_NONE)) {
+            const bool noInputChannel =
+                    info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
+            const bool canReceiveInput =
+                    !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
+                    !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
+            if (canReceiveInput && !noInputChannel) {
+                ALOGV("Window handle %s has no registered input channel",
+                      handle->getName().c_str());
+            }
+            continue;
+        }
+
+        if (info->displayId != displayId) {
+            ALOGE("Window %s updated by wrong display %d, should belong to display %d",
+                  handle->getName().c_str(), displayId, info->displayId);
+            continue;
+        }
+
+        if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) {
+            const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
+            oldHandle->updateFrom(handle);
+            newHandles.push_back(oldHandle);
+        } else {
+            newHandles.push_back(handle);
+        }
+    }
+
+    // Insert or replace
+    mWindowHandlesByDisplay[displayId] = newHandles;
+}
+
 /**
  * Called from InputManagerService, update window handle list by displayId that can receive input.
  * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
@@ -3152,9 +3561,13 @@
 void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles,
                                       int32_t displayId,
                                       const sp<ISetInputWindowsListener>& setInputWindowsListener) {
-#if DEBUG_FOCUS
-    ALOGD("setInputWindows displayId=%" PRId32, displayId);
-#endif
+    if (DEBUG_FOCUS) {
+        std::string windowList;
+        for (const sp<InputWindowHandle>& iwh : inputWindowHandles) {
+            windowList += iwh->getName() + " ";
+        }
+        ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -3162,73 +3575,19 @@
         const std::vector<sp<InputWindowHandle>> oldWindowHandles =
                 getWindowHandlesLocked(displayId);
 
+        updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId);
+
         sp<InputWindowHandle> newFocusedWindowHandle = nullptr;
         bool foundHoveredWindow = false;
-
-        if (inputWindowHandles.empty()) {
-            // 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 std::vector<sp<InputWindowHandle>>& oldHandles =
-                    mWindowHandlesByDisplay[displayId];
-            std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
-            for (const sp<InputWindowHandle>& handle : oldHandles) {
-                oldHandlesByTokens[handle->getToken()] = handle;
+        for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) {
+            // Set newFocusedWindowHandle to the top most focused window instead of the last one
+            if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus &&
+                windowHandle->getInfo()->visible) {
+                newFocusedWindowHandle = windowHandle;
             }
-
-            std::vector<sp<InputWindowHandle>> newHandles;
-            for (const sp<InputWindowHandle>& handle : inputWindowHandles) {
-                if (!handle->updateInfo()) {
-                    // handle no longer valid
-                    continue;
-                }
-                const InputWindowInfo* info = handle->getInfo();
-
-                if ((getInputChannelLocked(handle->getToken()) == nullptr &&
-                     info->portalToDisplayId == ADISPLAY_ID_NONE)) {
-                    const bool noInputChannel =
-                            info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL;
-                    const bool canReceiveInput =
-                            !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) ||
-                            !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE);
-                    if (canReceiveInput && !noInputChannel) {
-                        ALOGE("Window handle %s has no registered input channel",
-                              handle->getName().c_str());
-                    }
-                    continue;
-                }
-
-                if (info->displayId != displayId) {
-                    ALOGE("Window %s updated by wrong display %d, should belong to display %d",
-                          handle->getName().c_str(), displayId, info->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 (const sp<InputWindowHandle>& windowHandle : newHandles) {
-                // 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) {
@@ -3238,27 +3597,29 @@
         sp<InputWindowHandle> oldFocusedWindowHandle =
                 getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
 
-        if (oldFocusedWindowHandle != newFocusedWindowHandle) {
+        if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
             if (oldFocusedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
-                ALOGD("Focus left window: %s in display %" PRId32,
-                      oldFocusedWindowHandle->getName().c_str(), displayId);
-#endif
+                if (DEBUG_FOCUS) {
+                    ALOGD("Focus left window: %s in display %" PRId32,
+                          oldFocusedWindowHandle->getName().c_str(), displayId);
+                }
                 sp<InputChannel> focusedInputChannel =
                         getInputChannelLocked(oldFocusedWindowHandle->getToken());
                 if (focusedInputChannel != nullptr) {
                     CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                                                "focus left window");
                     synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
+                    enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/);
                 }
                 mFocusedWindowHandlesByDisplay.erase(displayId);
             }
             if (newFocusedWindowHandle != nullptr) {
-#if DEBUG_FOCUS
-                ALOGD("Focus entered window: %s in display %" PRId32,
-                      newFocusedWindowHandle->getName().c_str(), displayId);
-#endif
+                if (DEBUG_FOCUS) {
+                    ALOGD("Focus entered window: %s in display %" PRId32,
+                          newFocusedWindowHandle->getName().c_str(), displayId);
+                }
                 mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle;
+                enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/);
             }
 
             if (mFocusedDisplayId == displayId) {
@@ -3272,10 +3633,10 @@
             for (size_t i = 0; i < state.windows.size();) {
                 TouchedWindow& touchedWindow = state.windows[i];
                 if (!hasWindowHandleLocked(touchedWindow.windowHandle)) {
-#if DEBUG_FOCUS
-                    ALOGD("Touched window was removed: %s in display %" PRId32,
-                          touchedWindow.windowHandle->getName().c_str(), displayId);
-#endif
+                    if (DEBUG_FOCUS) {
+                        ALOGD("Touched window was removed: %s in display %" PRId32,
+                              touchedWindow.windowHandle->getName().c_str(), displayId);
+                    }
                     sp<InputChannel> touchedInputChannel =
                             getInputChannelLocked(touchedWindow.windowHandle->getToken());
                     if (touchedInputChannel != nullptr) {
@@ -3297,9 +3658,9 @@
         // which might not happen until the next GC.
         for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) {
             if (!hasWindowHandleLocked(oldWindowHandle)) {
-#if DEBUG_FOCUS
-                ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
-#endif
+                if (DEBUG_FOCUS) {
+                    ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
+                }
                 oldWindowHandle->releaseChannel();
             }
         }
@@ -3315,9 +3676,10 @@
 
 void InputDispatcher::setFocusedApplication(
         int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) {
-#if DEBUG_FOCUS
-    ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
+              inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -3335,10 +3697,6 @@
             oldFocusedApplicationHandle.clear();
             mFocusedApplicationHandlesByDisplay.erase(displayId);
         }
-
-#if DEBUG_FOCUS
-        // logDispatchStateLocked();
-#endif
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3355,9 +3713,9 @@
  * display. The display-specified events won't be affected.
  */
 void InputDispatcher::setFocusedDisplay(int32_t displayId) {
-#if DEBUG_FOCUS
-    ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
+    }
     { // acquire lock
         std::scoped_lock _l(mLock);
 
@@ -3396,9 +3754,9 @@
             }
         }
 
-#if DEBUG_FOCUS
-        logDispatchStateLocked();
-#endif
+        if (DEBUG_FOCUS) {
+            logDispatchStateLocked();
+        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3406,9 +3764,9 @@
 }
 
 void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) {
-#if DEBUG_FOCUS
-    ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen);
+    }
 
     bool changed;
     { // acquire lock
@@ -3430,9 +3788,9 @@
             changed = false;
         }
 
-#if DEBUG_FOCUS
-        logDispatchStateLocked();
-#endif
+        if (DEBUG_FOCUS) {
+            logDispatchStateLocked();
+        }
     } // release lock
 
     if (changed) {
@@ -3442,9 +3800,9 @@
 }
 
 void InputDispatcher::setInputFilterEnabled(bool enabled) {
-#if DEBUG_FOCUS
-    ALOGD("setInputFilterEnabled: enabled=%d", enabled);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("setInputFilterEnabled: enabled=%d", enabled);
+    }
 
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -3461,11 +3819,16 @@
     mLooper->wake();
 }
 
+void InputDispatcher::setInTouchMode(bool inTouchMode) {
+    std::scoped_lock lock(mLock);
+    mInTouchMode = inTouchMode;
+}
+
 bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
     if (fromToken == toToken) {
-#if DEBUG_FOCUS
-        ALOGD("Trivial transfer to same window.");
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGD("Trivial transfer to same window.");
+        }
         return true;
     }
 
@@ -3478,14 +3841,14 @@
             ALOGW("Cannot transfer focus because from or to window not found.");
             return false;
         }
-#if DEBUG_FOCUS
-        ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
-              fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
-#endif
+        if (DEBUG_FOCUS) {
+            ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s",
+                  fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str());
+        }
         if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) {
-#if DEBUG_FOCUS
-            ALOGD("Cannot transfer focus because windows are on different displays.");
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Cannot transfer focus because windows are on different displays.");
+            }
             return false;
         }
 
@@ -3513,30 +3876,26 @@
     Found:
 
         if (!found) {
-#if DEBUG_FOCUS
-            ALOGD("Focus transfer failed because from window did not have focus.");
-#endif
+            if (DEBUG_FOCUS) {
+                ALOGD("Focus transfer failed because from window did not have focus.");
+            }
             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) {
-            sp<Connection> fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex);
-            sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex);
-
-            fromConnection->inputState.copyPointerStateTo(toConnection->inputState);
+        sp<Connection> fromConnection = getConnectionLocked(fromToken);
+        sp<Connection> toConnection = getConnectionLocked(toToken);
+        if (fromConnection != nullptr && toConnection != nullptr) {
+            fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
             CancelationOptions
                     options(CancelationOptions::CANCEL_POINTER_EVENTS,
                             "transferring touch focus from this window to another window");
             synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
+            synthesizePointerDownEventsForConnectionLocked(toConnection);
         }
 
-#if DEBUG_FOCUS
-        logDispatchStateLocked();
-#endif
+        if (DEBUG_FOCUS) {
+            logDispatchStateLocked();
+        }
     } // release lock
 
     // Wake up poll loop since it may need to make new input dispatching choices.
@@ -3545,9 +3904,9 @@
 }
 
 void InputDispatcher::resetAndDropEverythingLocked(const char* reason) {
-#if DEBUG_FOCUS
-    ALOGD("Resetting and dropping all events (%s).", reason);
-#endif
+    if (DEBUG_FOCUS) {
+        ALOGD("Resetting and dropping all events (%s).", reason);
+    }
 
     CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason);
     synthesizeCancelationEventsForAllConnectionsLocked(options);
@@ -3652,12 +4011,10 @@
 
                     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, "
+                                                 "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
+                                                 "flags=0x%08x, type=0x%08x, "
                                                  "frame=[%d,%d][%d,%d], globalScale=%f, "
-                                                 "windowScale=(%f,%f), "
-                                                 "touchableRegion=",
+                                                 "windowScale=(%f,%f), touchableRegion=",
                                          i, windowInfo->name.c_str(), windowInfo->displayId,
                                          windowInfo->portalToDisplayId,
                                          toString(windowInfo->paused),
@@ -3666,11 +4023,10 @@
                                          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);
+                                         windowInfo->layoutParamsType, 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",
@@ -3703,9 +4059,9 @@
     nsecs_t currentTime = now();
 
     // Dump recently dispatched or dropped events from oldest to newest.
-    if (!mRecentQueue.isEmpty()) {
-        dump += StringPrintf(INDENT "RecentQueue: length=%u\n", mRecentQueue.count());
-        for (EventEntry* entry = mRecentQueue.head; entry; entry = entry->next) {
+    if (!mRecentQueue.empty()) {
+        dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size());
+        for (EventEntry* entry : mRecentQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
             dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
@@ -3726,9 +4082,9 @@
     }
 
     // Dump inbound events from oldest to newest.
-    if (!mInboundQueue.isEmpty()) {
-        dump += StringPrintf(INDENT "InboundQueue: length=%u\n", mInboundQueue.count());
-        for (EventEntry* entry = mInboundQueue.head; entry; entry = entry->next) {
+    if (!mInboundQueue.empty()) {
+        dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size());
+        for (EventEntry* entry : mInboundQueue) {
             dump += INDENT2;
             entry->appendDescription(dump);
             dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f);
@@ -3749,22 +4105,21 @@
         dump += INDENT "ReplacedKeys: <empty>\n";
     }
 
-    if (!mConnectionsByFd.isEmpty()) {
+    if (!mConnectionsByFd.empty()) {
         dump += INDENT "Connections:\n";
-        for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-            const sp<Connection>& connection = mConnectionsByFd.valueAt(i);
-            dump += StringPrintf(INDENT2 "%zu: channelName='%s', windowName='%s', "
+        for (const auto& pair : mConnectionsByFd) {
+            const sp<Connection>& connection = pair.second;
+            dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', "
                                          "status=%s, monitor=%s, inputPublisherBlocked=%s\n",
-                                 i, connection->getInputChannelName().c_str(),
+                                 pair.first, connection->getInputChannelName().c_str(),
                                  connection->getWindowName().c_str(), connection->getStatusLabel(),
                                  toString(connection->monitor),
                                  toString(connection->inputPublisherBlocked));
 
-            if (!connection->outboundQueue.isEmpty()) {
-                dump += StringPrintf(INDENT3 "OutboundQueue: length=%u\n",
-                                     connection->outboundQueue.count());
-                for (DispatchEntry* entry = connection->outboundQueue.head; entry;
-                     entry = entry->next) {
+            if (!connection->outboundQueue.empty()) {
+                dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n",
+                                     connection->outboundQueue.size());
+                for (DispatchEntry* entry : connection->outboundQueue) {
                     dump.append(INDENT4);
                     entry->eventEntry->appendDescription(dump);
                     dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n",
@@ -3775,11 +4130,10 @@
                 dump += INDENT3 "OutboundQueue: <empty>\n";
             }
 
-            if (!connection->waitQueue.isEmpty()) {
-                dump += StringPrintf(INDENT3 "WaitQueue: length=%u\n",
-                                     connection->waitQueue.count());
-                for (DispatchEntry* entry = connection->waitQueue.head; entry;
-                     entry = entry->next) {
+            if (!connection->waitQueue.empty()) {
+                dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n",
+                                     connection->waitQueue.size());
+                for (DispatchEntry* entry : connection->waitQueue) {
                     dump += INDENT4;
                     entry->eventEntry->appendDescription(dump);
                     dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, "
@@ -3819,17 +4173,15 @@
     }
 }
 
-status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
-                                               int32_t displayId) {
+status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
 #if DEBUG_REGISTRATION
-    ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32,
-          inputChannel->getName().c_str(), displayId);
+    ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
 #endif
 
     { // acquire lock
         std::scoped_lock _l(mLock);
-
-        if (getConnectionIndexLocked(inputChannel) >= 0) {
+        sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
+        if (existingConnection != nullptr) {
             ALOGW("Attempted to register already registered input channel '%s'",
                   inputChannel->getName().c_str());
             return BAD_VALUE;
@@ -3838,8 +4190,8 @@
         sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);
 
         int fd = inputChannel->getFd();
-        mConnectionsByFd.add(fd, connection);
-        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+        mConnectionsByFd[fd] = connection;
+        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
 
         mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     } // release lock
@@ -3859,7 +4211,7 @@
             return BAD_VALUE;
         }
 
-        if (inputChannel->getToken() == nullptr) {
+        if (inputChannel->getConnectionToken() == nullptr) {
             ALOGW("Attempted to register input monitor without an identifying token.");
             return BAD_VALUE;
         }
@@ -3867,8 +4219,8 @@
         sp<Connection> connection = new Connection(inputChannel, true /*monitor*/);
 
         const int fd = inputChannel->getFd();
-        mConnectionsByFd.add(fd, connection);
-        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
+        mConnectionsByFd[fd] = connection;
+        mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
 
         auto& monitorsByDisplay =
                 isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay;
@@ -3903,17 +4255,16 @@
 
 status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
                                                        bool notify) {
-    ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
-    if (connectionIndex < 0) {
+    sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken());
+    if (connection == nullptr) {
         ALOGW("Attempted to unregister already unregistered input channel '%s'",
               inputChannel->getName().c_str());
         return BAD_VALUE;
     }
 
-    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
-    mConnectionsByFd.removeItemsAt(connectionIndex);
-
-    mInputChannelsByToken.erase(inputChannel->getToken());
+    [[maybe_unused]] const bool removed = removeByValue(mConnectionsByFd, connection);
+    ALOG_ASSERT(removed);
+    mInputChannelsByToken.erase(inputChannel->getConnectionToken());
 
     if (connection->monitor) {
         removeMonitorChannelLocked(inputChannel);
@@ -3973,7 +4324,7 @@
         TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex);
         std::optional<int32_t> foundDeviceId;
         for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) {
-            if (touchedMonitor.monitor.inputChannel->getToken() == token) {
+            if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) {
                 foundDeviceId = state.deviceId;
             }
         }
@@ -3991,7 +4342,9 @@
         options.displayId = displayId;
         for (const TouchedWindow& window : state.windows) {
             sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
-            synthesizeCancelationEventsForInputChannelLocked(channel, options);
+            if (channel != nullptr) {
+                synthesizeCancelationEventsForInputChannelLocked(channel, options);
+            }
         }
         // Then clear the current touch state so we stop dispatching to them as well.
         state.filterNonMonitors();
@@ -4004,7 +4357,7 @@
     for (const auto& it : mGestureMonitorsByDisplay) {
         const std::vector<Monitor>& monitors = it.second;
         for (const Monitor& monitor : monitors) {
-            if (monitor.inputChannel->getToken() == token) {
+            if (monitor.inputChannel->getConnectionToken() == token) {
                 return it.first;
             }
         }
@@ -4012,30 +4365,31 @@
     return std::nullopt;
 }
 
-ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
-    if (inputChannel == nullptr) {
-        return -1;
+sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) {
+    if (inputConnectionToken == nullptr) {
+        return nullptr;
     }
 
-    for (size_t i = 0; i < mConnectionsByFd.size(); i++) {
-        sp<Connection> connection = mConnectionsByFd.valueAt(i);
-        if (connection->inputChannel->getToken() == inputChannel->getToken()) {
-            return i;
+    for (const auto& pair : mConnectionsByFd) {
+        const sp<Connection>& connection = pair.second;
+        if (connection->inputChannel->getConnectionToken() == inputConnectionToken) {
+            return connection;
         }
     }
 
-    return -1;
+    return nullptr;
 }
 
 void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime,
                                                     const sp<Connection>& connection, uint32_t seq,
                                                     bool handled) {
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doDispatchCycleFinishedLockedInterruptible);
     commandEntry->connection = connection;
     commandEntry->eventTime = currentTime;
     commandEntry->seq = seq;
     commandEntry->handled = handled;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime,
@@ -4043,19 +4397,21 @@
     ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
           connection->getInputChannelName().c_str());
 
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
     commandEntry->connection = connection;
+    postCommandLocked(std::move(commandEntry));
 }
 
 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);
+    std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
+            &InputDispatcher::doNotifyFocusChangedLockedInterruptible);
     commandEntry->oldToken = oldToken;
     commandEntry->newToken = newToken;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::onANRLocked(nsecs_t currentTime,
@@ -4086,12 +4442,13 @@
     mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason);
     dumpDispatchStateLocked(mLastANRState);
 
-    CommandEntry* commandEntry =
-            postCommandLocked(&InputDispatcher::doNotifyANRLockedInterruptible);
+    std::unique_ptr<CommandEntry> commandEntry =
+            std::make_unique<CommandEntry>(&InputDispatcher::doNotifyANRLockedInterruptible);
     commandEntry->inputApplicationHandle = applicationHandle;
     commandEntry->inputChannel =
             windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr;
     commandEntry->reason = reason;
+    postCommandLocked(std::move(commandEntry));
 }
 
 void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) {
@@ -4108,7 +4465,7 @@
     if (connection->status != Connection::STATUS_ZOMBIE) {
         mLock.unlock();
 
-        mPolicy->notifyInputChannelBroken(connection->inputChannel->getToken());
+        mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
 
         mLock.lock();
     }
@@ -4123,31 +4480,29 @@
 }
 
 void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) {
+    sp<IBinder> token =
+            commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr;
     mLock.unlock();
 
     nsecs_t newTimeout =
-            mPolicy->notifyANR(commandEntry->inputApplicationHandle,
-                               commandEntry->inputChannel ? commandEntry->inputChannel->getToken()
-                                                          : nullptr,
-                               commandEntry->reason);
+            mPolicy->notifyANR(commandEntry->inputApplicationHandle, token, commandEntry->reason);
 
     mLock.lock();
 
-    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel);
+    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, token);
 }
 
 void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible(
         CommandEntry* commandEntry) {
     KeyEntry* entry = commandEntry->keyEntry;
 
-    KeyEvent event;
-    initializeKeyEvent(&event, entry);
+    KeyEvent event = createKeyEvent(*entry);
 
     mLock.unlock();
 
     android::base::Timer t;
     sp<IBinder> token = commandEntry->inputChannel != nullptr
-            ? commandEntry->inputChannel->getToken()
+            ? commandEntry->inputChannel->getConnectionToken()
             : nullptr;
     nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags);
     if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
@@ -4176,53 +4531,58 @@
 
 void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) {
     sp<Connection> connection = commandEntry->connection;
-    nsecs_t finishTime = commandEntry->eventTime;
+    const nsecs_t finishTime = commandEntry->eventTime;
     uint32_t seq = commandEntry->seq;
-    bool handled = commandEntry->handled;
+    const bool handled = commandEntry->handled;
 
     // Handle post-event policy actions.
-    DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq);
-    if (dispatchEntry) {
-        nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
-        if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
-            std::string msg =
-                    StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
-                                 connection->getWindowName().c_str(), eventDuration * 0.000001f);
-            dispatchEntry->eventEntry->appendDescription(msg);
-            ALOGI("%s", msg.c_str());
-        }
-
-        bool restartEvent;
-        if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
-            KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
-            restartEvent =
-                    afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
-        } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
-            MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
-            restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry,
-                                                               motionEntry, handled);
-        } else {
-            restartEvent = false;
-        }
-
-        // Dequeue the event and start the next cycle.
-        // Note that because the lock might have been released, it is possible that the
-        // contents of the wait queue to have been drained, so we need to double-check
-        // a few things.
-        if (dispatchEntry == connection->findWaitQueueEntry(seq)) {
-            connection->waitQueue.dequeue(dispatchEntry);
-            traceWaitQueueLength(connection);
-            if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
-                connection->outboundQueue.enqueueAtHead(dispatchEntry);
-                traceOutboundQueueLength(connection);
-            } else {
-                releaseDispatchEntry(dispatchEntry);
-            }
-        }
-
-        // Start the next dispatch cycle for this connection.
-        startDispatchCycleLocked(now(), connection);
+    std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt == connection->waitQueue.end()) {
+        return;
     }
+    DispatchEntry* dispatchEntry = *dispatchEntryIt;
+
+    nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime;
+    if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) {
+        std::string msg =
+                StringPrintf("Window '%s' spent %0.1fms processing the last input event: ",
+                             connection->getWindowName().c_str(), eventDuration * 0.000001f);
+        dispatchEntry->eventEntry->appendDescription(msg);
+        ALOGI("%s", msg.c_str());
+    }
+
+    bool restartEvent;
+    if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
+        KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
+        restartEvent =
+                afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
+    } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
+        MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
+        restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
+                                                           handled);
+    } else {
+        restartEvent = false;
+    }
+
+    // Dequeue the event and start the next cycle.
+    // Note that because the lock might have been released, it is possible that the
+    // contents of the wait queue to have been drained, so we need to double-check
+    // a few things.
+    dispatchEntryIt = connection->findWaitQueueEntry(seq);
+    if (dispatchEntryIt != connection->waitQueue.end()) {
+        dispatchEntry = *dispatchEntryIt;
+        connection->waitQueue.erase(dispatchEntryIt);
+        traceWaitQueueLength(connection);
+        if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+            connection->outboundQueue.push_front(dispatchEntry);
+            traceOutboundQueueLength(connection);
+        } else {
+            releaseDispatchEntry(dispatchEntry);
+        }
+    }
+
+    // Start the next dispatch cycle for this connection.
+    startDispatchCycleLocked(now(), connection);
 }
 
 bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection,
@@ -4256,13 +4616,12 @@
                   keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount,
                   keyEntry->policyFlags);
 #endif
-            KeyEvent event;
-            initializeKeyEvent(&event, keyEntry);
+            KeyEvent event = createKeyEvent(*keyEntry);
             event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED);
 
             mLock.unlock();
 
-            mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
+            mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event,
                                           keyEntry->policyFlags, &event);
 
             mLock.lock();
@@ -4299,13 +4658,13 @@
               "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x",
               keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags);
 #endif
-        KeyEvent event;
-        initializeKeyEvent(&event, keyEntry);
+        KeyEvent event = createKeyEvent(*keyEntry);
 
         mLock.unlock();
 
-        bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event,
-                                                      keyEntry->policyFlags, &event);
+        bool fallback =
+                mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(),
+                                              &event, keyEntry->policyFlags, &event);
 
         mLock.lock();
 
@@ -4417,21 +4776,51 @@
     mLock.lock();
 }
 
-void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) {
-    event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags,
-                      entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount,
-                      entry->downTime, entry->eventTime);
+KeyEvent InputDispatcher::createKeyEvent(const KeyEntry& entry) {
+    KeyEvent event;
+    event.initialize(entry.deviceId, entry.source, entry.displayId, INVALID_HMAC, entry.action,
+                     entry.flags, entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount,
+                     entry.downTime, entry.eventTime);
+    return event;
 }
 
-void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
+void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
                                                int32_t injectionResult,
                                                nsecs_t timeSpentWaitingForApplication) {
     // TODO Write some statistics about how long we spend waiting.
 }
 
+/**
+ * Report the touch event latency to the statsd server.
+ * Input events are reported for statistics if:
+ * - This is a touchscreen event
+ * - InputFilter is not enabled
+ * - Event is not injected or synthesized
+ *
+ * Statistics should be reported before calling addValue, to prevent a fresh new sample
+ * from getting aggregated with the "old" data.
+ */
+void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry)
+        REQUIRES(mLock) {
+    const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) &&
+            !(motionEntry.isSynthesized()) && !mInputFilterEnabled;
+    if (!reportForStatistics) {
+        return;
+    }
+
+    if (mTouchStatistics.shouldReport()) {
+        android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(),
+                                   mTouchStatistics.getMax(), mTouchStatistics.getMean(),
+                                   mTouchStatistics.getStDev(), mTouchStatistics.getCount());
+        mTouchStatistics.reset();
+    }
+    const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime);
+    mTouchStatistics.addValue(latencyMicros);
+}
+
 void InputDispatcher::traceInboundQueueLengthLocked() {
     if (ATRACE_ENABLED()) {
-        ATRACE_INT("iq", mInboundQueue.count());
+        ATRACE_INT("iq", mInboundQueue.size());
     }
 }
 
@@ -4439,7 +4828,7 @@
     if (ATRACE_ENABLED()) {
         char counterName[40];
         snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str());
-        ATRACE_INT(counterName, connection->outboundQueue.count());
+        ATRACE_INT(counterName, connection->outboundQueue.size());
     }
 }
 
@@ -4447,7 +4836,7 @@
     if (ATRACE_ENABLED()) {
         char counterName[40];
         snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str());
-        ATRACE_INT(counterName, connection->waitQueue.count());
+        ATRACE_INT(counterName, connection->waitQueue.size());
     }
 }
 
@@ -4470,4 +4859,22 @@
     mDispatcherIsAlive.wait(_l);
 }
 
+/**
+ * Wake up the dispatcher and wait until it processes all events and commands.
+ * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so
+ * this method can be safely called from any thread, as long as you've ensured that
+ * the work you are interested in completing has already been queued.
+ */
+bool InputDispatcher::waitForIdle() {
+    /**
+     * Timeout should represent the longest possible time that a device might spend processing
+     * events and commands.
+     */
+    constexpr std::chrono::duration TIMEOUT = 100ms;
+    std::unique_lock lock(mLock);
+    mLooper->wake();
+    std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT);
+    return result == std::cv_status::no_timeout;
+}
+
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 67bf199..ded59a5 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -25,16 +25,16 @@
 #include "InputDispatcherPolicyInterface.h"
 #include "InputState.h"
 #include "InputTarget.h"
+#include "InputThread.h"
 #include "Monitor.h"
-#include "Queue.h"
 #include "TouchState.h"
 #include "TouchedWindow.h"
 
-#include <cutils/atomic.h>
 #include <input/Input.h>
 #include <input/InputApplication.h>
 #include <input/InputTransport.h>
 #include <input/InputWindow.h>
+#include <input/LatencyStatistics.h>
 #include <limits.h>
 #include <stddef.h>
 #include <ui/Region.h>
@@ -56,6 +56,16 @@
 
 class Connection;
 
+class HmacKeyManager {
+public:
+    HmacKeyManager();
+    std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
+
+private:
+    std::array<uint8_t, 32> sign(const std::vector<uint8_t>& data) const;
+    const std::array<uint8_t, 128> mHmacKey;
+};
+
 /* Dispatches events to input targets.  Some functions of the input dispatcher, such as
  * identifying input targets, are controlled by a separate policy object.
  *
@@ -82,8 +92,9 @@
 
     virtual void dump(std::string& dump) override;
     virtual void monitor() override;
-
-    virtual void dispatchOnce() override;
+    virtual bool waitForIdle() override;
+    virtual status_t start() override;
+    virtual status_t stop() override;
 
     virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
     virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -95,6 +106,8 @@
                                      int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
                                      uint32_t policyFlags) override;
 
+    virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
+
     virtual void setInputWindows(
             const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId,
             const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override;
@@ -103,51 +116,61 @@
     virtual void setFocusedDisplay(int32_t displayId) override;
     virtual void setInputDispatchMode(bool enabled, bool frozen) override;
     virtual void setInputFilterEnabled(bool enabled) override;
+    virtual void setInTouchMode(bool inTouchMode) override;
 
     virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
                                     const sp<IBinder>& toToken) override;
 
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-                                          int32_t displayId) override;
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override;
     virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId,
                                           bool isGestureMonitor) override;
     virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override;
     virtual status_t pilferPointers(const sp<IBinder>& token) override;
 
 private:
-
-    enum DropReason {
-        DROP_REASON_NOT_DROPPED = 0,
-        DROP_REASON_POLICY = 1,
-        DROP_REASON_APP_SWITCH = 2,
-        DROP_REASON_DISABLED = 3,
-        DROP_REASON_BLOCKED = 4,
-        DROP_REASON_STALE = 5,
+    enum class DropReason {
+        NOT_DROPPED,
+        POLICY,
+        APP_SWITCH,
+        DISABLED,
+        BLOCKED,
+        STALE,
     };
 
+    std::unique_ptr<InputThread> mThread;
+
     sp<InputDispatcherPolicyInterface> mPolicy;
     android::InputDispatcherConfiguration mConfig;
 
     std::mutex mLock;
 
     std::condition_variable mDispatcherIsAlive;
+    std::condition_variable mDispatcherEnteredIdle;
 
     sp<Looper> mLooper;
 
     EventEntry* mPendingEvent GUARDED_BY(mLock);
-    Queue<EventEntry> mInboundQueue GUARDED_BY(mLock);
-    Queue<EventEntry> mRecentQueue GUARDED_BY(mLock);
-    Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock);
+    std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock);
+    std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock);
+    std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock);
 
     DropReason mLastDropReason GUARDED_BY(mLock);
 
+    // With each iteration, InputDispatcher nominally processes one queued event,
+    // a timeout, or a response from an input consumer.
+    // This method should only be called on the input dispatcher's own thread.
+    void dispatchOnce();
+
     void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
 
     // Enqueues an inbound event.  Returns true if mLooper->wake() should be called.
     bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock);
 
     // Cleans up input state when dropping an inbound event.
-    void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock);
+    void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock);
+
+    // Enqueues a focus event.
+    void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
     void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock);
@@ -156,13 +179,10 @@
     bool mAppSwitchSawKeyDown GUARDED_BY(mLock);
     nsecs_t mAppSwitchDueTime GUARDED_BY(mLock);
 
-    bool isAppSwitchKeyEvent(KeyEntry* keyEntry);
+    bool isAppSwitchKeyEvent(const KeyEntry& keyEntry);
     bool isAppSwitchPendingLocked() REQUIRES(mLock);
     void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock);
 
-    // Stale event latency optimization.
-    static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry);
-
     // Blocked event latency optimization.  Drops old events when the user intends
     // to transfer focus to a new application.
     EventEntry* mNextUnblockedEvent GUARDED_BY(mLock);
@@ -172,7 +192,7 @@
                                                     bool addPortalWindows = false) REQUIRES(mLock);
 
     // All registered connections mapped by channel file descriptor.
-    KeyedVector<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
+    std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
 
     struct IBinderHash {
         std::size_t operator()(const sp<IBinder>& b) const {
@@ -186,7 +206,7 @@
     std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token)
             REQUIRES(mLock);
 
-    ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock);
+    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) REQUIRES(mLock);
 
     // Input channels that will receive a copy of all input events sent to the provided display.
     std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
@@ -235,7 +255,7 @@
     // Deferred command processing.
     bool haveCommandsLocked() const REQUIRES(mLock);
     bool runCommandsLockedInterruptible() REQUIRES(mLock);
-    CommandEntry* postCommandLocked(Command command) REQUIRES(mLock);
+    void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock);
 
     // Input filter processing.
     bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock);
@@ -250,6 +270,7 @@
     bool mDispatchEnabled GUARDED_BY(mLock);
     bool mDispatchFrozen GUARDED_BY(mLock);
     bool mInputFilterEnabled GUARDED_BY(mLock);
+    bool mInTouchMode GUARDED_BY(mLock);
 
     std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
             GUARDED_BY(mLock);
@@ -261,6 +282,13 @@
     sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock);
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
 
+    /*
+     * Validate and update InputWindowHandles for a given display.
+     */
+    void updateWindowHandlesForDisplayLocked(
+            const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
+            REQUIRES(mLock);
+
     // Focus tracking for keys, trackball, etc.
     std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay
             GUARDED_BY(mLock);
@@ -286,11 +314,12 @@
                            nsecs_t* nextWakeupTime) REQUIRES(mLock);
     bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason,
                               nsecs_t* nextWakeupTime) REQUIRES(mLock);
+    void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock);
     void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry,
                              const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
 
-    void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry);
-    void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry);
+    void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
+    void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
 
     // Keeping track of ANR timeouts.
     enum InputTargetWaitCause {
@@ -309,7 +338,7 @@
     sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock);
 
     // Finding targets for input events.
-    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
+    int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry& entry,
                                         const sp<InputApplicationHandle>& applicationHandle,
                                         const sp<InputWindowHandle>& windowHandle,
                                         nsecs_t* nextWakeupTime, const char* reason)
@@ -318,16 +347,16 @@
     void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
 
     void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
-                                                 const sp<InputChannel>& inputChannel)
+                                                 const sp<IBinder>& inputConnectionToken)
             REQUIRES(mLock);
     nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock);
     void resetANRTimeoutsLocked() REQUIRES(mLock);
 
-    int32_t getTargetDisplayId(const EventEntry* entry);
-    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
+    int32_t getTargetDisplayId(const EventEntry& entry);
+    int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry,
                                            std::vector<InputTarget>& inputTargets,
                                            nsecs_t* nextWakeupTime) REQUIRES(mLock);
-    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
+    int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry,
                                            std::vector<InputTarget>& inputTargets,
                                            nsecs_t* nextWakeupTime,
                                            bool* outConflictingPointerActions) REQUIRES(mLock);
@@ -346,7 +375,7 @@
     void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
                                           float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
 
-    void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock);
+    void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
     bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
                                   const InjectionState* injectionState);
     bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x,
@@ -357,7 +386,7 @@
 
     std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
                                                    const sp<InputWindowHandle>& windowHandle,
-                                                   const EventEntry* eventEntry,
+                                                   const EventEntry& eventEntry,
                                                    const char* targetType) REQUIRES(mLock);
 
     // Manage the dispatch cycle for a single connection.
@@ -365,13 +394,13 @@
     // with the mutex held makes it easier to ensure that connection invariants are maintained.
     // If needed, the methods post commands to run later once the critical bits are done.
     void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                    EventEntry* eventEntry, const InputTarget* inputTarget)
+                                    EventEntry* eventEntry, const InputTarget& inputTarget)
             REQUIRES(mLock);
     void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
-                                      EventEntry* eventEntry, const InputTarget* inputTarget)
+                                      EventEntry* eventEntry, const InputTarget& inputTarget)
             REQUIRES(mLock);
     void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
-                                    const InputTarget* inputTarget, int32_t dispatchMode)
+                                    const InputTarget& inputTarget, int32_t dispatchMode)
             REQUIRES(mLock);
     void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
@@ -379,7 +408,7 @@
                                    uint32_t seq, bool handled) REQUIRES(mLock);
     void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
                                         bool notify) REQUIRES(mLock);
-    void drainDispatchQueue(Queue<DispatchEntry>* queue);
+    void drainDispatchQueue(std::deque<DispatchEntry*>& queue);
     void releaseDispatchEntry(DispatchEntry* dispatchEntry);
     static int handleReceiveCallback(int fd, int events, void* data);
     // The action sent should only be of type AMOTION_EVENT_*
@@ -400,8 +429,11 @@
                                                         const CancelationOptions& options)
             REQUIRES(mLock);
 
+    void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection)
+            REQUIRES(mLock);
+
     // Splitting motion events across windows.
-    MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds);
+    MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds);
 
     // Reset and drop everything the dispatcher is doing.
     void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock);
@@ -446,11 +478,15 @@
                                              DispatchEntry* dispatchEntry, MotionEntry* motionEntry,
                                              bool handled) REQUIRES(mLock);
     void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
-    void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
+    KeyEvent createKeyEvent(const KeyEntry& entry);
     void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
 
     // Statistics gathering.
-    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry,
+    static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min;
+    LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD};
+
+    void reportTouchEventForStatistics(const MotionEntry& entry);
+    void updateDispatchStatistics(nsecs_t currentTime, const EventEntry& entry,
                                   int32_t injectionResult, nsecs_t timeSpentWaitingForApplication);
     void traceInboundQueueLengthLocked() REQUIRES(mLock);
     void traceOutboundQueueLength(const sp<Connection>& connection);
diff --git a/services/inputflinger/dispatcher/InputDispatcherThread.cpp b/services/inputflinger/dispatcher/InputDispatcherThread.cpp
deleted file mode 100644
index 18b1b8c..0000000
--- a/services/inputflinger/dispatcher/InputDispatcherThread.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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 "InputDispatcherThread.h"
-
-#include "InputDispatcherInterface.h"
-
-namespace android {
-
-InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher)
-      : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {}
-
-InputDispatcherThread::~InputDispatcherThread() {}
-
-bool InputDispatcherThread::threadLoop() {
-    mDispatcher->dispatchOnce();
-    return true;
-}
-
-} // namespace android
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 7d9b03a..053598a 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -36,12 +36,12 @@
     return false;
 }
 
-bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) {
+bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) {
     switch (action) {
         case AKEY_EVENT_ACTION_UP: {
-            if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+            if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) {
                 for (size_t i = 0; i < mFallbackKeys.size();) {
-                    if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+                    if (mFallbackKeys.valueAt(i) == entry.keyCode) {
                         mFallbackKeys.removeItemsAt(i);
                     } else {
                         i += 1;
@@ -65,7 +65,7 @@
     #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
                     "keyCode=%d, scanCode=%d",
-                    entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
+                    entry.deviceId, entry.source, entry.keyCode, entry.scanCode);
     #endif
             return false;
             */
@@ -86,7 +86,7 @@
     }
 }
 
-bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) {
+bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
     int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
     switch (actionMasked) {
         case AMOTION_EVENT_ACTION_UP:
@@ -99,7 +99,7 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
                   "displayId=%" PRId32 ", actionMasked=%d",
-                  entry->deviceId, entry->source, entry->displayId, actionMasked);
+                  entry.deviceId, entry.source, entry.displayId, actionMasked);
 #endif
             return false;
         }
@@ -116,7 +116,7 @@
         case AMOTION_EVENT_ACTION_POINTER_UP:
         case AMOTION_EVENT_ACTION_POINTER_DOWN:
         case AMOTION_EVENT_ACTION_MOVE: {
-            if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) {
+            if (entry.source & AINPUT_SOURCE_CLASS_NAVIGATION) {
                 // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need
                 // to generate cancellation events for these since they're based in relative rather
                 // than absolute units.
@@ -125,35 +125,38 @@
 
             ssize_t index = findMotionMemento(entry, false /*hovering*/);
 
-            if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) {
+            if (entry.source & AINPUT_SOURCE_CLASS_JOYSTICK) {
                 // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
                 // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral.
                 // Any other value and we need to track the motion so we can send cancellation
                 // events for anything generating fallback events (e.g. DPad keys for joystick
                 // movements).
                 if (index >= 0) {
-                    if (entry->pointerCoords[0].isEmpty()) {
+                    if (entry.pointerCoords[0].isEmpty()) {
                         mMotionMementos.erase(mMotionMementos.begin() + index);
                     } else {
                         MotionMemento& memento = mMotionMementos[index];
                         memento.setPointers(entry);
                     }
-                } else if (!entry->pointerCoords[0].isEmpty()) {
+                } else if (!entry.pointerCoords[0].isEmpty()) {
                     addMotionMemento(entry, flags, false /*hovering*/);
                 }
 
                 // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
                 return true;
             }
+
             if (index >= 0) {
                 MotionMemento& memento = mMotionMementos[index];
-                memento.setPointers(entry);
-                return true;
+                if (memento.firstNewPointerIdx < 0) {
+                    memento.setPointers(entry);
+                    return true;
+                }
             }
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent motion pointer up/down or move event: "
                   "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
-                  entry->deviceId, entry->source, entry->displayId, actionMasked);
+                  entry.deviceId, entry.source, entry.displayId, actionMasked);
 #endif
             return false;
         }
@@ -167,7 +170,7 @@
 #if DEBUG_OUTBOUND_EVENT_DETAILS
             ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
                   "displayId=%" PRId32,
-                  entry->deviceId, entry->source, entry->displayId);
+                  entry.deviceId, entry.source, entry.displayId);
 #endif
             return false;
         }
@@ -187,77 +190,90 @@
     }
 }
 
-ssize_t InputState::findKeyMemento(const KeyEntry* entry) const {
+ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
     for (size_t i = 0; i < mKeyMementos.size(); i++) {
         const KeyMemento& memento = mKeyMementos[i];
-        if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
-            memento.displayId == entry->displayId && memento.keyCode == entry->keyCode &&
-            memento.scanCode == entry->scanCode) {
+        if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
+            memento.displayId == entry.displayId && memento.keyCode == entry.keyCode &&
+            memento.scanCode == entry.scanCode) {
             return i;
         }
     }
     return -1;
 }
 
-ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const {
+ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
         const MotionMemento& memento = mMotionMementos[i];
-        if (memento.deviceId == entry->deviceId && memento.source == entry->source &&
-            memento.displayId == entry->displayId && memento.hovering == hovering) {
+        if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
+            memento.displayId == entry.displayId && memento.hovering == hovering) {
             return i;
         }
     }
     return -1;
 }
 
-void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
+void InputState::addKeyMemento(const KeyEntry& entry, int32_t flags) {
     KeyMemento memento;
-    memento.deviceId = entry->deviceId;
-    memento.source = entry->source;
-    memento.displayId = entry->displayId;
-    memento.keyCode = entry->keyCode;
-    memento.scanCode = entry->scanCode;
-    memento.metaState = entry->metaState;
+    memento.deviceId = entry.deviceId;
+    memento.source = entry.source;
+    memento.displayId = entry.displayId;
+    memento.keyCode = entry.keyCode;
+    memento.scanCode = entry.scanCode;
+    memento.metaState = entry.metaState;
     memento.flags = flags;
-    memento.downTime = entry->downTime;
-    memento.policyFlags = entry->policyFlags;
+    memento.downTime = entry.downTime;
+    memento.policyFlags = entry.policyFlags;
     mKeyMementos.push_back(memento);
 }
 
-void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) {
+void InputState::addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering) {
     MotionMemento memento;
-    memento.deviceId = entry->deviceId;
-    memento.source = entry->source;
-    memento.displayId = entry->displayId;
+    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.xPrecision = entry.xPrecision;
+    memento.yPrecision = entry.yPrecision;
+    memento.xCursorPosition = entry.xCursorPosition;
+    memento.yCursorPosition = entry.yCursorPosition;
+    memento.downTime = entry.downTime;
     memento.setPointers(entry);
     memento.hovering = hovering;
-    memento.policyFlags = entry->policyFlags;
+    memento.policyFlags = entry.policyFlags;
     mMotionMementos.push_back(memento);
 }
 
-void InputState::MotionMemento::setPointers(const MotionEntry* entry) {
-    pointerCount = entry->pointerCount;
-    for (uint32_t i = 0; i < entry->pointerCount; i++) {
-        pointerProperties[i].copyFrom(entry->pointerProperties[i]);
-        pointerCoords[i].copyFrom(entry->pointerCoords[i]);
+void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
+    pointerCount = entry.pointerCount;
+    for (uint32_t i = 0; i < entry.pointerCount; i++) {
+        pointerProperties[i].copyFrom(entry.pointerProperties[i]);
+        pointerCoords[i].copyFrom(entry.pointerCoords[i]);
     }
 }
 
-void InputState::synthesizeCancelationEvents(nsecs_t currentTime,
-                                             std::vector<EventEntry*>& outEvents,
-                                             const CancelationOptions& options) {
+void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const {
+    for (uint32_t i = 0; i < pointerCount; i++) {
+        if (other.firstNewPointerIdx < 0) {
+            other.firstNewPointerIdx = other.pointerCount;
+        }
+        other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
+        other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
+        other.pointerCount++;
+    }
+}
+
+std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
+        nsecs_t currentTime, const CancelationOptions& options) {
+    std::vector<EventEntry*> events;
     for (KeyMemento& memento : mKeyMementos) {
         if (shouldCancelKey(memento, options)) {
-            outEvents.push_back(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));
+            events.push_back(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 /*repeatCount*/,
+                                          memento.downTime));
         }
     }
 
@@ -265,17 +281,73 @@
         if (shouldCancelMotion(memento, options)) {
             const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
                                                     : AMOTION_EVENT_ACTION_CANCEL;
-            outEvents.push_back(
-                    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.pointerCount,
-                                    memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/,
-                                    0 /*yOffset*/));
+            events.push_back(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.xCursorPosition,
+                                             memento.yCursorPosition, memento.downTime,
+                                             memento.pointerCount, memento.pointerProperties,
+                                             memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
         }
     }
+    return events;
+}
+
+std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
+    std::vector<EventEntry*> events;
+    for (MotionMemento& memento : mMotionMementos) {
+        if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
+            continue;
+        }
+
+        if (memento.firstNewPointerIdx < 0) {
+            continue;
+        }
+
+        uint32_t pointerCount = 0;
+        PointerProperties pointerProperties[MAX_POINTERS];
+        PointerCoords pointerCoords[MAX_POINTERS];
+
+        // We will deliver all pointers the target already knows about
+        for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
+            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
+            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+            pointerCount++;
+        }
+
+        // We will send explicit events for all pointers the target doesn't know about
+        for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
+                i < memento.pointerCount; i++) {
+
+            pointerProperties[i].copyFrom(memento.pointerProperties[i]);
+            pointerCoords[i].copyFrom(memento.pointerCoords[i]);
+            pointerCount++;
+
+            // Down only if the first pointer, pointer down otherwise
+            const int32_t action = (pointerCount <= 1)
+                    ? AMOTION_EVENT_ACTION_DOWN
+                    : AMOTION_EVENT_ACTION_POINTER_DOWN
+                            | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+            events.push_back(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.xCursorPosition,
+                                             memento.yCursorPosition, memento.downTime,
+                                             pointerCount, pointerProperties,
+                                             pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
+        }
+
+        memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
+    }
+
+    return events;
 }
 
 void InputState::clear() {
@@ -284,21 +356,27 @@
     mFallbackKeys.clear();
 }
 
-void InputState::copyPointerStateTo(InputState& other) const {
+void InputState::mergePointerStateTo(InputState& other) {
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
-        const MotionMemento& memento = mMotionMementos[i];
+        MotionMemento& memento = mMotionMementos[i];
+        // Since we support split pointers we need to merge touch events
+        // from the same source + device + screen.
         if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
-            for (size_t j = 0; j < other.mMotionMementos.size();) {
-                const MotionMemento& otherMemento = other.mMotionMementos[j];
+            bool merged = false;
+            for (size_t j = 0; j < other.mMotionMementos.size(); j++) {
+                MotionMemento& otherMemento = other.mMotionMementos[j];
                 if (memento.deviceId == otherMemento.deviceId &&
                     memento.source == otherMemento.source &&
                     memento.displayId == otherMemento.displayId) {
-                    other.mMotionMementos.erase(other.mMotionMementos.begin() + j);
-                } else {
-                    j += 1;
+                    memento.mergePointerStateTo(otherMemento);
+                    merged = true;
+                    break;
                 }
             }
-            other.mMotionMementos.push_back(memento);
+            if (!merged) {
+                memento.firstNewPointerIdx = 0;
+                other.mMotionMementos.push_back(memento);
+            }
         }
     }
 }
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index 205b647..08266ae 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -24,8 +24,7 @@
 
 namespace android::inputdispatcher {
 
-// Sequence number for synthesized or injected events.
-constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
+static constexpr int32_t INVALID_POINTER_INDEX = -1;
 
 /* Tracks dispatched key and motion event state so that cancellation events can be
  * synthesized when events are dropped. */
@@ -44,22 +43,25 @@
     // Records tracking information for a key event that has just been published.
     // Returns true if the event should be delivered, false if it is inconsistent
     // and should be skipped.
-    bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
+    bool trackKey(const KeyEntry& entry, int32_t action, int32_t flags);
 
     // Records tracking information for a motion event that has just been published.
     // Returns true if the event should be delivered, false if it is inconsistent
     // and should be skipped.
-    bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
+    bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags);
 
     // Synthesizes cancelation events for the current state and resets the tracked state.
-    void synthesizeCancelationEvents(nsecs_t currentTime, std::vector<EventEntry*>& outEvents,
-                                     const CancelationOptions& options);
+    std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime,
+                                                         const CancelationOptions& options);
+
+    // Synthesizes down events for the current state.
+    std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime);
 
     // Clears the current state.
     void clear();
 
-    // Copies pointer-related parts of the input state to another instance.
-    void copyPointerStateTo(InputState& other) const;
+    // Merges pointer-related parts of the input state into another instance.
+    void mergePointerStateTo(InputState& other);
 
     // Gets the fallback key associated with a keycode.
     // Returns -1 if none.
@@ -94,25 +96,30 @@
         int32_t flags;
         float xPrecision;
         float yPrecision;
+        float xCursorPosition;
+        float yCursorPosition;
         nsecs_t downTime;
         uint32_t pointerCount;
         PointerProperties pointerProperties[MAX_POINTERS];
         PointerCoords pointerCoords[MAX_POINTERS];
+        // Track for which pointers the target doesn't know about.
+        int32_t firstNewPointerIdx = INVALID_POINTER_INDEX;
         bool hovering;
         uint32_t policyFlags;
 
-        void setPointers(const MotionEntry* entry);
+        void setPointers(const MotionEntry& entry);
+        void mergePointerStateTo(MotionMemento& other) const;
     };
 
     std::vector<KeyMemento> mKeyMementos;
     std::vector<MotionMemento> mMotionMementos;
     KeyedVector<int32_t, int32_t> mFallbackKeys;
 
-    ssize_t findKeyMemento(const KeyEntry* entry) const;
-    ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
+    ssize_t findKeyMemento(const KeyEntry& entry) const;
+    ssize_t findMotionMemento(const MotionEntry& entry, bool hovering) const;
 
-    void addKeyMemento(const KeyEntry* entry, int32_t flags);
-    void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
+    void addKeyMemento(const KeyEntry& entry, int32_t flags);
+    void addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering);
 
     static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options);
     static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options);
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 80fa2cb..0588374 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,4 +42,63 @@
     return StringPrintf("%" PRId32, dispatchMode);
 }
 
+void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
+                              float windowXScale, float windowYScale) {
+    // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
+    // and non splittable windows since we will just use all the pointers from the input event.
+    if (newPointerIds.isEmpty()) {
+        setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+        return;
+    }
+
+    // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
+    ALOG_ASSERT((pointerIds & newPointerIds) == 0);
+
+    pointerIds |= newPointerIds;
+    while (!newPointerIds.isEmpty()) {
+        int32_t pointerId = newPointerIds.clearFirstMarkedBit();
+        pointerInfos[pointerId].xOffset = xOffset;
+        pointerInfos[pointerId].yOffset = yOffset;
+        pointerInfos[pointerId].windowXScale = windowXScale;
+        pointerInfos[pointerId].windowYScale = windowYScale;
+    }
+}
+
+void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+                                        float windowYScale) {
+    pointerIds.clear();
+    pointerInfos[0].xOffset = xOffset;
+    pointerInfos[0].yOffset = yOffset;
+    pointerInfos[0].windowXScale = windowXScale;
+    pointerInfos[0].windowYScale = windowYScale;
+}
+
+bool InputTarget::useDefaultPointerInfo() const {
+    return pointerIds.isEmpty();
+}
+
+const PointerInfo& InputTarget::getDefaultPointerInfo() const {
+    return pointerInfos[0];
+}
+
+std::string InputTarget::getPointerInfoString() const {
+    if (useDefaultPointerInfo()) {
+        const PointerInfo& pointerInfo = getDefaultPointerInfo();
+        return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)",
+                            pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale,
+                            pointerInfo.windowYScale);
+    }
+
+    std::string out;
+    for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) {
+        if (!pointerIds.hasBit(i)) {
+            continue;
+        }
+        out += StringPrintf("\n  pointerId %d: xOffset=%.1f, yOffset=%.1f "
+                            "windowScaleFactor=(%.1f, %.1f)",
+                            i, pointerInfos[i].xOffset, pointerInfos[i].yOffset,
+                            pointerInfos[i].windowXScale, pointerInfos[i].windowYScale);
+    }
+    return out;
+}
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 5acf92b..499a75f 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -24,6 +24,22 @@
 namespace android::inputdispatcher {
 
 /*
+ * Information about each pointer for an InputTarget. This includes offset and scale so
+ * all pointers can be normalized to a single offset and scale.
+ *
+ * These values are ignored for KeyEvents
+ */
+struct PointerInfo {
+    // The x and y offset to add to a MotionEvent as it is delivered.
+    float xOffset = 0.0f;
+    float yOffset = 0.0f;
+
+    // Scaling factor to apply to MotionEvent as it is delivered.
+    float windowXScale = 1.0f;
+    float windowYScale = 1.0f;
+};
+
+/*
  * An input target specifies how an input event is to be dispatched to a particular window
  * including the window's input channel, control flags, a timeout, and an X / Y offset to
  * be added to input event coordinates to compensate for the absolute position of the
@@ -93,21 +109,39 @@
     sp<InputChannel> inputChannel;
 
     // Flags for the input target.
-    int32_t flags;
-
-    // The x and y offset to add to a MotionEvent as it is delivered.
-    // (ignored for KeyEvents)
-    float xOffset, yOffset;
+    int32_t flags = 0;
 
     // Scaling factor to apply to MotionEvent as it is delivered.
     // (ignored for KeyEvents)
-    float globalScaleFactor;
-    float windowXScale = 1.0f;
-    float windowYScale = 1.0f;
+    float globalScaleFactor = 1.0f;
 
     // The subset of pointer ids to include in motion events dispatched to this input target
     // if FLAG_SPLIT is set.
     BitSet32 pointerIds;
+    // The data is stored by the pointerId. Use the bit position of pointerIds to look up
+    // PointerInfo per pointerId.
+    PointerInfo pointerInfos[MAX_POINTERS];
+
+    void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
+                     float windowYScale);
+    void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+                               float windowYScale);
+
+    /**
+     * Returns whether the default pointer information should be used. This will be true when the
+     * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors
+     * and non splittable windows since we want all pointers for the EventEntry to go to this
+     * target.
+     */
+    bool useDefaultPointerInfo() const;
+
+    /**
+     * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+     * true.
+     */
+    const PointerInfo& getDefaultPointerInfo() const;
+
+    std::string getPointerInfoString() const;
 };
 
 std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/dispatcher/Queue.h b/services/inputflinger/dispatcher/Queue.h
deleted file mode 100644
index 0e75821..0000000
--- a/services/inputflinger/dispatcher/Queue.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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_INPUTDISPATCHER_QUEUE_H
-#define _UI_INPUT_INPUTDISPATCHER_QUEUE_H
-
-namespace android::inputdispatcher {
-
-// Generic queue implementation.
-template <typename T>
-struct Queue {
-    T* head;
-    T* tail;
-    uint32_t entryCount;
-
-    inline Queue() : head(nullptr), tail(nullptr), entryCount(0) {}
-
-    inline bool isEmpty() const { return !head; }
-
-    inline void enqueueAtTail(T* entry) {
-        entryCount++;
-        entry->prev = tail;
-        if (tail) {
-            tail->next = entry;
-        } else {
-            head = entry;
-        }
-        entry->next = nullptr;
-        tail = entry;
-    }
-
-    inline void enqueueAtHead(T* entry) {
-        entryCount++;
-        entry->next = head;
-        if (head) {
-            head->prev = entry;
-        } else {
-            tail = entry;
-        }
-        entry->prev = nullptr;
-        head = entry;
-    }
-
-    inline void dequeue(T* entry) {
-        entryCount--;
-        if (entry->prev) {
-            entry->prev->next = entry->next;
-        } else {
-            head = entry->next;
-        }
-        if (entry->next) {
-            entry->next->prev = entry->prev;
-        } else {
-            tail = entry->prev;
-        }
-    }
-
-    inline T* dequeueAtHead() {
-        entryCount--;
-        T* entry = head;
-        head = entry->next;
-        if (head) {
-            head->prev = nullptr;
-        } else {
-            tail = nullptr;
-        }
-        return entry;
-    }
-
-    uint32_t count() const { return entryCount; }
-};
-
-} // namespace android::inputdispatcher
-
-#endif // _UI_INPUT_INPUTDISPATCHER_QUEUE_H
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 18848a0..2baceba 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -93,15 +93,6 @@
                            std::end(newMonitors));
 }
 
-void TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) {
-    for (size_t i = 0; i < windows.size(); i++) {
-        if (windows[i].windowHandle == windowHandle) {
-            windows.erase(windows.begin() + i);
-            return;
-        }
-    }
-}
-
 void TouchState::removeWindowByToken(const sp<IBinder>& token) {
     for (size_t i = 0; i < windows.size(); i++) {
         if (windows[i].windowHandle->getToken() == token) {
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 3e0e0eb..623c6a8 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -49,7 +49,6 @@
                            BitSet32 pointerIds);
     void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle);
     void addGestureMonitors(const std::vector<TouchedMonitor>& monitors);
-    void removeWindow(const sp<android::InputWindowHandle>& windowHandle);
     void removeWindowByToken(const sp<IBinder>& token);
     void filterNonAsIsTouchWindows();
     void filterNonMonitors();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 9329ca6..6e98676 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -63,12 +63,23 @@
     /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
     virtual void monitor() = 0;
 
-    /* Runs a single iteration of the dispatch loop.
-     * Nominally processes one queued event, a timeout, or a response from an input consumer.
-     *
-     * This method should only be called on the input dispatcher thread.
+    /**
+     * Wait until dispatcher is idle. That means, there are no further events to be processed,
+     * and all of the policy callbacks have been completed.
+     * Return true if the dispatcher is idle.
+     * Return false if the timeout waiting for the dispatcher to become idle has expired.
      */
-    virtual void dispatchOnce() = 0;
+    virtual bool waitForIdle() = 0;
+
+    /* Make the dispatcher start processing events.
+     *
+     * The dispatcher will start consuming events from the InputListenerInterface
+     * in the order that they were received.
+     */
+    virtual status_t start() = 0;
+
+    /* Makes the dispatcher stop processing events. */
+    virtual status_t stop() = 0;
 
     /* Injects an input event and optionally waits for sync.
      * The synchronization mode determines whether the method blocks while waiting for
@@ -81,6 +92,13 @@
                                      int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis,
                                      uint32_t policyFlags) = 0;
 
+    /*
+     * Check whether InputEvent actually happened by checking the signature of the event.
+     *
+     * Return nullptr if the event cannot be verified.
+     */
+    virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) = 0;
+
     /* Sets the list of input windows.
      *
      * This method may be called on any thread (usually by the input manager).
@@ -116,6 +134,14 @@
      */
     virtual void setInputFilterEnabled(bool enabled) = 0;
 
+    /**
+     * Set the touch mode state.
+     * Touch mode is a global state that apps may enter / exit based on specific
+     * user interactions with input devices.
+     * If true, the device is in touch mode.
+     */
+    virtual void setInTouchMode(bool inTouchMode) = 0;
+
     /* Transfers touch focus from one window to another window.
      *
      * Returns true on success.  False if the window did not actually have touch focus.
@@ -126,8 +152,7 @@
      *
      * This method may be called on any thread (usually by the input manager).
      */
-    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel,
-                                          int32_t displayId) = 0;
+    virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0;
 
     /* Registers input channels to be used to monitor input events.
      *
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h
deleted file mode 100644
index 2604959..0000000
--- a/services/inputflinger/dispatcher/include/InputDispatcherThread.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
-
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class InputDispatcherInterface;
-
-/* Enqueues and dispatches input events, endlessly. */
-class InputDispatcherThread : public Thread {
-public:
-    explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
-    ~InputDispatcherThread();
-
-private:
-    virtual bool threadLoop();
-
-    sp<InputDispatcherInterface> mDispatcher;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h
index b51dcb6..0dcd2f9 100644
--- a/services/inputflinger/include/InputListener.h
+++ b/services/inputflinger/include/InputListener.h
@@ -107,31 +107,32 @@
      */
     MotionClassification classification;
     int32_t edgeFlags;
-    /**
-     * A timestamp in the input device's time base, not the platform's.
-     * The units are microseconds since the last reset.
-     * This can only be compared to other device timestamps from the same device.
-     * This value will overflow after a little over an hour.
-     */
-    uint32_t deviceTimestamp;
+
     uint32_t pointerCount;
     PointerProperties pointerProperties[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
     float xPrecision;
     float yPrecision;
+    /**
+     * Mouse cursor position when this event is reported relative to the origin of the specified
+     * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in
+     * gestures enabled mode.
+     */
+    float xCursorPosition;
+    float yCursorPosition;
     nsecs_t downTime;
     std::vector<TouchVideoFrame> videoFrames;
 
     inline 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, MotionClassification classification,
-            int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount,
-            const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
-            float xPrecision, float yPrecision, nsecs_t downTime,
-            const std::vector<TouchVideoFrame>& videoFrames);
+                     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, uint32_t pointerCount,
+                     const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
+                     float xPrecision, float yPrecision, float xCursorPosition,
+                     float yCursorPosition, nsecs_t downTime,
+                     const std::vector<TouchVideoFrame>& videoFrames);
 
     NotifyMotionArgs(const NotifyMotionArgs& other);
 
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 8ad5dd0..f8d5351 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,19 +19,18 @@
 
 #include "PointerControllerInterface.h"
 
+#include <input/DisplayViewport.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/Errors.h>
 #include <utils/RefBase.h>
-#include <utils/SortedVector.h>
 
-#include <optional>
 #include <stddef.h>
 #include <unistd.h>
+#include <optional>
+#include <set>
 #include <unordered_map>
 #include <vector>
 
@@ -45,7 +44,16 @@
 
 namespace android {
 
-/* Processes raw input events and sends cooked event data to an input listener. */
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
 class InputReaderInterface : public virtual RefBase {
 protected:
     InputReaderInterface() { }
@@ -57,18 +65,17 @@
      * 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. */
+    /* Called by the heartbeat 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;
+    /* Makes the reader start processing events from the kernel. */
+    virtual status_t start() = 0;
+
+    /* Makes the reader stop processing any more events. */
+    virtual status_t stop() = 0;
 
     /* Gets information about all input devices.
      *
@@ -105,17 +112,7 @@
     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();
-};
+// --- InputReaderConfiguration ---
 
 /*
  * Input reader configuration.
@@ -172,6 +169,9 @@
     // Used to determine which DisplayViewport should be tied to which InputDevice.
     std::unordered_map<std::string, uint8_t> portAssociations;
 
+    // The suggested display ID to show the cursor.
+    int32_t defaultPointerDisplayId;
+
     // Velocity control parameters for mouse pointer movements.
     VelocityControlParameters pointerVelocityControlParameters;
 
@@ -250,7 +250,7 @@
     bool pointerCapture;
 
     // The set of currently disabled input devices.
-    SortedVector<int32_t> disabledDevices;
+    std::set<int32_t> disabledDevices;
 
     InputReaderConfiguration() :
             virtualKeyQuietTime(0),
@@ -270,10 +270,13 @@
             pointerGestureZoomSpeedRatio(0.3f),
             showTouches(false), pointerCapture(false) { }
 
+    static std::string changesToString(uint32_t changes);
+
     std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
     std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
             const;
     std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
+    std::optional<DisplayViewport> getDisplayViewportById(int32_t displayId) const;
     void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
 
 
@@ -284,6 +287,8 @@
     std::vector<DisplayViewport> mDisplays;
 };
 
+// --- TouchAffineTransformation ---
+
 struct TouchAffineTransformation {
     float x_scale;
     float x_ymix;
@@ -306,6 +311,8 @@
     void applyTo(float& x, float& y) const;
 };
 
+// --- InputReaderPolicyInterface ---
+
 /*
  * Input reader policy interface.
  *
@@ -315,8 +322,8 @@
  * 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.
+ * These methods will NOT re-enter the input reader interface, so they may be called from
+ * any method in the input reader interface.
  */
 class InputReaderPolicyInterface : public virtual RefBase {
 protected:
@@ -349,4 +356,4 @@
 
 } // namespace android
 
-#endif // _UI_INPUT_READER_COMMON_H
\ No newline at end of file
+#endif // _UI_INPUT_READER_COMMON_H
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
new file mode 100644
index 0000000..407365a
--- /dev/null
+++ b/services/inputflinger/include/InputThread.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_THREAD_H
+#define _UI_INPUT_THREAD_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+/* A thread that loops continuously until destructed to process input events.
+ *
+ * Creating the InputThread starts it immediately. The thread begins looping the loop
+ * function until the InputThread is destroyed. The wake function is used to wake anything
+ * that sleeps in the loop when it is time for the thread to be destroyed.
+ */
+class InputThread {
+public:
+    explicit InputThread(std::string name, std::function<void()> loop,
+                         std::function<void()> wake = nullptr);
+    virtual ~InputThread();
+
+    bool isCallingThread();
+
+private:
+    std::string mName;
+    std::function<void()> mThreadWake;
+    sp<Thread> mThread;
+};
+
+} // namespace android
+
+#endif // _UI_INPUT_THREAD_H
\ No newline at end of file
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index 0ff28e4..194c665 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -17,6 +17,7 @@
 #ifndef _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
 #define _INPUTFLINGER_POINTER_CONTROLLER_INTERFACE_H
 
+#include <input/DisplayViewport.h>
 #include <input/Input.h>
 #include <utils/BitSet.h>
 #include <utils/RefBase.h>
@@ -101,6 +102,9 @@
 
     /* Gets the id of the display where the pointer should be shown. */
     virtual int32_t getDisplayId() const = 0;
+
+    /* Sets the associated display of this pointer. Pointer should show on that display. */
+    virtual void setDisplayViewport(const DisplayViewport& displayViewport) = 0;
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 4e97397..23c08b2 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -50,6 +50,7 @@
 
     shared_libs: [
         "libbase",
+        "libcap",
         "libinputflinger_base",
         "libcrypto",
         "libcutils",
@@ -57,8 +58,6 @@
         "liblog",
         "libui",
         "libutils",
-        "libhardware_legacy",
-        "libstatslog",
     ],
 
     header_libs: [
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index a5e5415..264d287 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -24,11 +24,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/capability.h>
 #include <sys/epoll.h>
 #include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <sys/limits.h>
-#include <sys/utsname.h>
 #include <unistd.h>
 
 #define LOG_TAG "EventHub"
@@ -37,8 +37,6 @@
 
 #include "EventHub.h"
 
-#include <hardware_legacy/power.h>
-
 #include <android-base/stringprintf.h>
 #include <cutils/properties.h>
 #include <openssl/sha.h>
@@ -71,7 +69,6 @@
 
 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";
@@ -94,14 +91,6 @@
     return out;
 }
 
-static void getLinuxRelease(int* major, int* minor) {
-    struct utsname info;
-    if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) {
-        *major = 0, *minor = 0;
-        ALOGE("Could not get linux version: %s", strerror(errno));
-    }
-}
-
 /**
  * Return true if name matches "v4l-touch*"
  */
@@ -246,6 +235,47 @@
     return !isVirtual && enabled;
 }
 
+/**
+ * Get the capabilities for the current process.
+ * Crashes the system if unable to create / check / destroy the capabilities object.
+ */
+class Capabilities final {
+public:
+    explicit Capabilities() {
+        mCaps = cap_get_proc();
+        LOG_ALWAYS_FATAL_IF(mCaps == nullptr, "Could not get capabilities of the current process");
+    }
+
+    /**
+     * Check whether the current process has a specific capability
+     * in the set of effective capabilities.
+     * Return CAP_SET if the process has the requested capability
+     * Return CAP_CLEAR otherwise.
+     */
+    cap_flag_value_t checkEffectiveCapability(cap_value_t capability) {
+        cap_flag_value_t value;
+        const int result = cap_get_flag(mCaps, capability, CAP_EFFECTIVE, &value);
+        LOG_ALWAYS_FATAL_IF(result == -1, "Could not obtain the requested capability");
+        return value;
+    }
+
+    ~Capabilities() {
+        const int result = cap_free(mCaps);
+        LOG_ALWAYS_FATAL_IF(result == -1, "Could not release the capabilities structure");
+    }
+
+private:
+    cap_t mCaps;
+};
+
+static void ensureProcessCanBlockSuspend() {
+    Capabilities capabilities;
+    const bool canBlockSuspend =
+            capabilities.checkEffectiveCapability(CAP_BLOCK_SUSPEND) == CAP_SET;
+    LOG_ALWAYS_FATAL_IF(!canBlockSuspend,
+                        "Input must be able to block suspend to properly process events");
+}
+
 // --- EventHub ---
 
 const int EventHub::EPOLL_MAX_EVENTS;
@@ -262,7 +292,7 @@
         mPendingEventCount(0),
         mPendingEventIndex(0),
         mPendingINotify(false) {
-    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
+    ensureProcessCanBlockSuspend();
 
     mEpollFd = epoll_create1(EPOLL_CLOEXEC);
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
@@ -280,9 +310,8 @@
         ALOGI("Video device scanning disabled");
     }
 
-    struct epoll_event eventItem;
-    memset(&eventItem, 0, sizeof(eventItem));
-    eventItem.events = EPOLLIN;
+    struct epoll_event eventItem = {};
+    eventItem.events = EPOLLIN | EPOLLWAKEUP;
     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);
@@ -306,11 +335,6 @@
     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);
-
-    int major, minor;
-    getLinuxRelease(&major, &minor);
-    // EPOLLWAKEUP was introduced in kernel 3.5
-    mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5);
 }
 
 EventHub::~EventHub(void) {
@@ -326,8 +350,6 @@
     ::close(mINotifyFd);
     ::close(mWakeReadPipeFd);
     ::close(mWakeWritePipeFd);
-
-    release_wake_lock(WAKE_LOCK_ID);
 }
 
 InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
@@ -1018,26 +1040,24 @@
             break;
         }
 
-        // Poll for events.  Mind the wake lock dance!
-        // We hold a wake lock at all times except during epoll_wait().  This works due to some
-        // subtle choreography.  When a device driver has pending (unread) events, it acquires
-        // a kernel wake lock.  However, once the last pending event has been read, the device
-        // driver will release the kernel wake lock.  To prevent the system from going to sleep
-        // when this happens, the EventHub holds onto its own user wake lock while the client
-        // is processing events.  Thus the system can only sleep if there are no events
-        // pending or currently being processed.
+        // Poll for events.
+        // When a device driver has pending (unread) events, it acquires
+        // a kernel wake lock.  Once the last pending event has been read, the device
+        // driver will release the kernel wake lock, but the epoll will hold the wakelock,
+        // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
+        // is called again for the same fd that produced the event.
+        // Thus the system can only sleep if there are no events pending or
+        // currently being processed.
         //
         // The timeout is advisory only.  If the device is asleep, it will not wake just to
         // service the timeout.
         mPendingEventIndex = 0;
 
-        mLock.unlock(); // release lock before poll, must be before release_wake_lock
-        release_wake_lock(WAKE_LOCK_ID);
+        mLock.unlock(); // release lock before poll
 
         int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
 
-        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
-        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
+        mLock.lock(); // reacquire lock after poll
 
         if (pollResult == 0) {
             // Timed out.
@@ -1491,27 +1511,13 @@
         }
     }
 
-    std::string wakeMechanism = "EPOLLWAKEUP";
-    if (!mUsingEpollWakeup) {
-#ifndef EVIOCSSUSPENDBLOCK
-        // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels
-        // will use an epoll flag instead, so as long as we want to support
-        // this feature, we need to be prepared to define the ioctl ourselves.
-#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int)
-#endif
-        if (ioctl(device->fd, EVIOCSSUSPENDBLOCK, 1)) {
-            wakeMechanism = "<none>";
-        } else {
-            wakeMechanism = "EVIOCSSUSPENDBLOCK";
-        }
-    }
     // Tell the kernel that we want to use the monotonic clock for reporting timestamps
     // associated with input events.  This is important because the input system
     // uses the timestamps extensively and assumes they were recorded using the monotonic
     // clock.
     int clockId = CLOCK_MONOTONIC;
     bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId);
-    ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(), toString(usingClockIoctl));
+    ALOGI("usingClockIoctl=%s", toString(usingClockIoctl));
 }
 
 void EventHub::openVideoDeviceLocked(const std::string& devicePath) {
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index d0613b0..1c08ab1 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -18,7 +18,16 @@
 
 #include "InputDevice.h"
 
-#include "InputMapper.h"
+#include "CursorInputMapper.h"
+#include "ExternalStylusInputMapper.h"
+#include "InputReaderContext.h"
+#include "JoystickInputMapper.h"
+#include "KeyboardInputMapper.h"
+#include "MultiTouchInputMapper.h"
+#include "RotaryEncoderInputMapper.h"
+#include "SingleTouchInputMapper.h"
+#include "SwitchInputMapper.h"
+#include "VibratorInputMapper.h"
 
 namespace android {
 
@@ -36,19 +45,20 @@
         mHasMic(false),
         mDropUntilNextSync(false) {}
 
-InputDevice::~InputDevice() {
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        delete mMappers[i];
-    }
-    mMappers.clear();
-}
+InputDevice::~InputDevice() {}
 
 bool InputDevice::isEnabled() {
     return getEventHub()->isDeviceEnabled(mId);
 }
 
 void InputDevice::setEnabled(bool enabled, nsecs_t when) {
+    if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) {
+        ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", "
+              "but the corresponding viewport is not found",
+              getName().c_str(), *mAssociatedDisplayPort);
+        enabled = false;
+    }
+
     if (isEnabled() == enabled) {
         return;
     }
@@ -103,15 +113,80 @@
         }
     }
 
-    size_t numMappers = mMappers.size();
-    for (size_t i = 0; i < numMappers; i++) {
-        InputMapper* mapper = mMappers[i];
-        mapper->dump(dump);
-    }
+    for_each_mapper([&dump](InputMapper& mapper) { mapper.dump(dump); });
 }
 
-void InputDevice::addMapper(InputMapper* mapper) {
-    mMappers.push_back(mapper);
+void InputDevice::populateMappers() {
+    uint32_t classes = mClasses;
+    std::vector<std::unique_ptr<InputMapper>>& mappers = mMappers;
+
+    // External devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
+        setExternal(true);
+    }
+
+    // Devices with mics.
+    if (classes & INPUT_DEVICE_CLASS_MIC) {
+        setMic(true);
+    }
+
+    // Switch-like devices.
+    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
+        mappers.push_back(std::make_unique<SwitchInputMapper>(this));
+    }
+
+    // Scroll wheel-like devices.
+    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
+        mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(this));
+    }
+
+    // Vibrator-like devices.
+    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
+        mappers.push_back(std::make_unique<VibratorInputMapper>(this));
+    }
+
+    // Keyboard-like devices.
+    uint32_t keyboardSource = 0;
+    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
+    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
+        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
+        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
+    }
+    if (classes & INPUT_DEVICE_CLASS_DPAD) {
+        keyboardSource |= AINPUT_SOURCE_DPAD;
+    }
+    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
+        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
+    }
+
+    if (keyboardSource != 0) {
+        mappers.push_back(
+                std::make_unique<KeyboardInputMapper>(this, keyboardSource, keyboardType));
+    }
+
+    // Cursor-like devices.
+    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+        mappers.push_back(std::make_unique<CursorInputMapper>(this));
+    }
+
+    // Touchscreens and touchpad devices.
+    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
+        mappers.push_back(std::make_unique<MultiTouchInputMapper>(this));
+    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
+        mappers.push_back(std::make_unique<SingleTouchInputMapper>(this));
+    }
+
+    // Joystick-like devices.
+    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
+        mappers.push_back(std::make_unique<JoystickInputMapper>(this));
+    }
+
+    // External stylus-like devices.
+    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
+        mappers.push_back(std::make_unique<ExternalStylusInputMapper>(this));
+    }
 }
 
 void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config,
@@ -144,14 +219,15 @@
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) {
-            ssize_t index = config->disabledDevices.indexOf(mId);
-            bool enabled = index < 0;
+            auto it = config->disabledDevices.find(mId);
+            bool enabled = it == config->disabledDevices.end();
             setEnabled(enabled, when);
         }
 
         if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
             // In most situations, no port will be specified.
             mAssociatedDisplayPort = std::nullopt;
+            mAssociatedViewport = std::nullopt;
             // Find the display port that corresponds to the current input port.
             const std::string& inputPort = mIdentifier.location;
             if (!inputPort.empty()) {
@@ -161,19 +237,45 @@
                     mAssociatedDisplayPort = std::make_optional(displayPort->second);
                 }
             }
+
+            // If the device was explicitly disabled by the user, it would be present in the
+            // "disabledDevices" list. If it is associated with a specific display, and it was not
+            // explicitly disabled, then enable/disable the device based on whether we can find the
+            // corresponding viewport.
+            bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end());
+            if (mAssociatedDisplayPort) {
+                mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort);
+                if (!mAssociatedViewport) {
+                    ALOGW("Input device %s should be associated with display on port %" PRIu8 ", "
+                          "but the corresponding viewport is not found.",
+                          getName().c_str(), *mAssociatedDisplayPort);
+                    enabled = false;
+                }
+            }
+
+            if (changes) {
+                // For first-time configuration, only allow device to be disabled after mappers have
+                // finished configuring. This is because we need to read some of the properties from
+                // the device's open fd.
+                setEnabled(enabled, when);
+            }
         }
 
-        for (InputMapper* mapper : mMappers) {
-            mapper->configure(when, config, changes);
-            mSources |= mapper->getSources();
+        for_each_mapper([this, when, config, changes](InputMapper& mapper) {
+            mapper.configure(when, config, changes);
+            mSources |= mapper.getSources();
+        });
+
+        // If a device is just plugged but it might be disabled, we need to update some info like
+        // axis range of touch from each InputMapper first, then disable it.
+        if (!changes) {
+            setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when);
         }
     }
 }
 
 void InputDevice::reset(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->reset(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.reset(when); });
 
     mContext->updateGlobalMetaState();
 
@@ -208,32 +310,25 @@
             mDropUntilNextSync = true;
             reset(rawEvent->when);
         } else {
-            for (InputMapper* mapper : mMappers) {
-                mapper->process(rawEvent);
-            }
+            for_each_mapper([rawEvent](InputMapper& mapper) { mapper.process(rawEvent); });
         }
         --count;
     }
 }
 
 void InputDevice::timeoutExpired(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->timeoutExpired(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.timeoutExpired(when); });
 }
 
 void InputDevice::updateExternalStylusState(const StylusState& state) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateExternalStylusState(state);
-    }
+    for_each_mapper([state](InputMapper& mapper) { mapper.updateExternalStylusState(state); });
 }
 
 void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) {
     outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
                               mHasMic);
-    for (InputMapper* mapper : mMappers) {
-        mapper->populateDeviceInfo(outDeviceInfo);
-    }
+    for_each_mapper(
+            [outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
 }
 
 int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) {
@@ -250,11 +345,12 @@
 
 int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
+    for (auto& mapperPtr : mMappers) {
+        InputMapper& mapper = *mapperPtr;
+        if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
             // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
             // value.  Otherwise, return AKEY_STATE_UP as long as one mapper reports it.
-            int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code);
+            int32_t currentResult = (mapper.*getStateFunc)(sourceMask, code);
             if (currentResult >= AKEY_STATE_DOWN) {
                 return currentResult;
             } else if (currentResult == AKEY_STATE_UP) {
@@ -268,51 +364,41 @@
 bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
                                         const int32_t* keyCodes, uint8_t* outFlags) {
     bool result = false;
-    for (InputMapper* mapper : mMappers) {
-        if (sourcesMatchMask(mapper->getSources(), sourceMask)) {
-            result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
+    for_each_mapper([&result, sourceMask, numCodes, keyCodes, outFlags](InputMapper& mapper) {
+        if (sourcesMatchMask(mapper.getSources(), sourceMask)) {
+            result |= mapper.markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
         }
-    }
+    });
     return result;
 }
 
 void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
                           int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->vibrate(pattern, patternSize, repeat, token);
-    }
+    for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) {
+        mapper.vibrate(pattern, patternSize, repeat, token);
+    });
 }
 
 void InputDevice::cancelVibrate(int32_t token) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelVibrate(token);
-    }
+    for_each_mapper([token](InputMapper& mapper) { mapper.cancelVibrate(token); });
 }
 
 void InputDevice::cancelTouch(nsecs_t when) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->cancelTouch(when);
-    }
+    for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
 }
 
 int32_t InputDevice::getMetaState() {
     int32_t result = 0;
-    for (InputMapper* mapper : mMappers) {
-        result |= mapper->getMetaState();
-    }
+    for_each_mapper([&result](InputMapper& mapper) { result |= mapper.getMetaState(); });
     return result;
 }
 
 void InputDevice::updateMetaState(int32_t keyCode) {
-    for (InputMapper* mapper : mMappers) {
-        mapper->updateMetaState(keyCode);
-    }
+    for_each_mapper([keyCode](InputMapper& mapper) { mapper.updateMetaState(keyCode); });
 }
 
 void InputDevice::fadePointer() {
-    for (InputMapper* mapper : mMappers) {
-        mapper->fadePointer();
-    }
+    for_each_mapper([](InputMapper& mapper) { mapper.fadePointer(); });
 }
 
 void InputDevice::bumpGeneration() {
@@ -324,15 +410,15 @@
     mContext->getListener()->notifyDeviceReset(&args);
 }
 
-std::optional<int32_t> InputDevice::getAssociatedDisplay() {
-    for (InputMapper* mapper : mMappers) {
-        std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay();
-        if (associatedDisplayId) {
-            return associatedDisplayId;
-        }
+std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
+    // Check if we had associated to the specific display.
+    if (mAssociatedViewport) {
+        return mAssociatedViewport->displayId;
     }
 
-    return std::nullopt;
+    // No associated display port, check if some InputMapper is associated.
+    return first_in_mappers<int32_t>(
+            [](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); });
 }
 
 } // namespace android
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 27cbf19..010729a 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -18,37 +18,29 @@
 
 #include "InputReader.h"
 
-#include "CursorInputMapper.h"
-#include "ExternalStylusInputMapper.h"
-#include "InputReaderContext.h"
-#include "JoystickInputMapper.h"
-#include "KeyboardInputMapper.h"
-#include "MultiTouchInputMapper.h"
-#include "RotaryEncoderInputMapper.h"
-#include "SingleTouchInputMapper.h"
-#include "SwitchInputMapper.h"
-#include "VibratorInputMapper.h"
-
+#include <android-base/stringprintf.h>
 #include <errno.h>
+#include <input/Keyboard.h>
+#include <input/VirtualKeyMap.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <log/log.h>
 #include <math.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <utils/Errors.h>
+#include <utils/Thread.h>
 
-#include <log/log.h>
-
-#include <android-base/stringprintf.h>
-#include <input/Keyboard.h>
-#include <input/VirtualKeyMap.h>
-
+#include "InputDevice.h"
 
 using android::base::StringPrintf;
 
 namespace android {
 
-InputReader::InputReader(const sp<EventHubInterface>& eventHub,
+// --- InputReader ---
+
+InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                          const sp<InputReaderPolicyInterface>& policy,
                          const sp<InputListenerInterface>& listener)
       : mContext(this),
@@ -71,11 +63,29 @@
 }
 
 InputReader::~InputReader() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        delete mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        delete devicePair.second;
     }
 }
 
+status_t InputReader::start() {
+    if (mThread) {
+        return ALREADY_EXISTS;
+    }
+    mThread = std::make_unique<InputThread>(
+            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
+    return OK;
+}
+
+status_t InputReader::stop() {
+    if (mThread && mThread->isCallingThread()) {
+        ALOGE("InputReader cannot be stopped from its own thread!");
+        return INVALID_OPERATION;
+    }
+    mThread.reset();
+    return OK;
+}
+
 void InputReader::loopOnce() {
     int32_t oldGeneration;
     int32_t timeoutMillis;
@@ -179,8 +189,7 @@
 }
 
 void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
+    if (mDevices.find(deviceId) != mDevices.end()) {
         ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
         return;
     }
@@ -201,7 +210,7 @@
               device->getSources());
     }
 
-    mDevices.add(deviceId, device);
+    mDevices.insert({deviceId, device});
     bumpGenerationLocked();
 
     if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
@@ -210,15 +219,14 @@
 }
 
 void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
-    InputDevice* device = nullptr;
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
         return;
     }
 
-    device = mDevices.valueAt(deviceIndex);
-    mDevices.removeItemsAt(deviceIndex, 1);
+    InputDevice* device = deviceIt->second;
+    mDevices.erase(deviceIt);
     bumpGenerationLocked();
 
     if (device->isIgnored()) {
@@ -242,86 +250,19 @@
                                              uint32_t classes) {
     InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
                                           controllerNumber, identifier, classes);
-
-    // External devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
-        device->setExternal(true);
-    }
-
-    // Devices with mics.
-    if (classes & INPUT_DEVICE_CLASS_MIC) {
-        device->setMic(true);
-    }
-
-    // Switch-like devices.
-    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
-        device->addMapper(new SwitchInputMapper(device));
-    }
-
-    // Scroll wheel-like devices.
-    if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) {
-        device->addMapper(new RotaryEncoderInputMapper(device));
-    }
-
-    // Vibrator-like devices.
-    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
-        device->addMapper(new VibratorInputMapper(device));
-    }
-
-    // Keyboard-like devices.
-    uint32_t keyboardSource = 0;
-    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
-    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
-        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
-        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
-    }
-    if (classes & INPUT_DEVICE_CLASS_DPAD) {
-        keyboardSource |= AINPUT_SOURCE_DPAD;
-    }
-    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
-        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
-    }
-
-    if (keyboardSource != 0) {
-        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));
-    }
-
-    // Cursor-like devices.
-    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
-        device->addMapper(new CursorInputMapper(device));
-    }
-
-    // Touchscreens and touchpad devices.
-    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
-        device->addMapper(new MultiTouchInputMapper(device));
-    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
-        device->addMapper(new SingleTouchInputMapper(device));
-    }
-
-    // Joystick-like devices.
-    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {
-        device->addMapper(new JoystickInputMapper(device));
-    }
-
-    // External stylus-like devices.
-    if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
-        device->addMapper(new ExternalStylusInputMapper(device));
-    }
-
+    device->populateMappers();
     return device;
 }
 
 void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents,
                                                size_t count) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Discarding event for unknown deviceId %d.", deviceId);
         return;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = deviceIt->second;
     if (device->isIgnored()) {
         // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
         return;
@@ -331,8 +272,8 @@
 }
 
 void InputReader::timeoutExpiredLocked(nsecs_t when) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         if (!device->isIgnored()) {
             device->timeoutExpired(when);
         }
@@ -353,14 +294,15 @@
     mEventHub->setExcludedDevices(mConfig.excludedDeviceNames);
 
     if (changes) {
-        ALOGI("Reconfiguring input devices.  changes=0x%08x", changes);
+        ALOGI("Reconfiguring input devices, changes=%s",
+              InputReaderConfiguration::changesToString(changes).c_str());
         nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
 
         if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) {
             mEventHub->requestReopenDevices();
         } else {
-            for (size_t i = 0; i < mDevices.size(); i++) {
-                InputDevice* device = mDevices.valueAt(i);
+            for (auto& devicePair : mDevices) {
+                InputDevice* device = devicePair.second;
                 device->configure(now, &mConfig, changes);
             }
         }
@@ -370,8 +312,8 @@
 void InputReader::updateGlobalMetaStateLocked() {
     mGlobalMetaState = 0;
 
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         mGlobalMetaState |= device->getMetaState();
     }
 }
@@ -385,8 +327,8 @@
 }
 
 void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -396,8 +338,8 @@
 }
 
 void InputReader::dispatchExternalStylusState(const StylusState& state) {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         device->updateExternalStylusState(state);
     }
 }
@@ -420,8 +362,8 @@
 }
 
 void InputReader::fadePointerLocked() {
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         device->fadePointer();
     }
 }
@@ -445,9 +387,8 @@
 void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) {
     outInputDevices.clear();
 
-    size_t numDevices = mDevices.size();
-    for (size_t i = 0; i < numDevices; i++) {
-        InputDevice* device = mDevices.valueAt(i);
+    for (auto& devicePair : mDevices) {
+        InputDevice* device = devicePair.second;
         if (!device->isIgnored()) {
             InputDeviceInfo info;
             device->getDeviceInfo(&info);
@@ -478,17 +419,16 @@
                                     GetStateFunc getStateFunc) {
     int32_t result = AKEY_STATE_UNKNOWN;
     if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
+        auto deviceIt = mDevices.find(deviceId);
+        if (deviceIt != mDevices.end()) {
+            InputDevice* device = deviceIt->second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result = (device->*getStateFunc)(sourceMask, code);
             }
         }
     } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
+        for (auto& devicePair : mDevices) {
+            InputDevice* device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that
                 // value.  Otherwise, return AKEY_STATE_UP as long as one device reports it.
@@ -505,13 +445,13 @@
 }
 
 void InputReader::toggleCapsLockState(int32_t deviceId) {
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
         return;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
+    InputDevice* device = deviceIt->second;
     if (device->isIgnored()) {
         return;
     }
@@ -532,17 +472,16 @@
                                               uint8_t* outFlags) {
     bool result = false;
     if (deviceId >= 0) {
-        ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-        if (deviceIndex >= 0) {
-            InputDevice* device = mDevices.valueAt(deviceIndex);
+        auto deviceIt = mDevices.find(deviceId);
+        if (deviceIt != mDevices.end()) {
+            InputDevice* device = deviceIt->second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
             }
         }
     } else {
-        size_t numDevices = mDevices.size();
-        for (size_t i = 0; i < numDevices; i++) {
-            InputDevice* device = mDevices.valueAt(i);
+        for (auto& devicePair : mDevices) {
+            InputDevice* device = devicePair.second;
             if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) {
                 result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags);
             }
@@ -567,10 +506,9 @@
 void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
                           ssize_t repeat, int32_t token) {
     AutoMutex _l(mLock);
-
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt != mDevices.end()) {
+        InputDevice* device = deviceIt->second;
         device->vibrate(pattern, patternSize, repeat, token);
     }
 }
@@ -578,9 +516,9 @@
 void InputReader::cancelVibrate(int32_t deviceId, int32_t token) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt != mDevices.end()) {
+        InputDevice* device = deviceIt->second;
         device->cancelVibrate(token);
     }
 }
@@ -588,9 +526,9 @@
 bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex >= 0) {
-        InputDevice* device = mDevices.valueAt(deviceIndex);
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt != mDevices.end()) {
+        InputDevice* device = deviceIt->second;
         return device->isEnabled();
     }
     ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
@@ -600,21 +538,26 @@
 bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
     AutoMutex _l(mLock);
 
-    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
-    if (deviceIndex < 0) {
+    auto deviceIt = mDevices.find(deviceId);
+    if (deviceIt == mDevices.end()) {
         ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
         return false;
     }
 
-    InputDevice* device = mDevices.valueAt(deviceIndex);
-    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay();
+    InputDevice* device = deviceIt->second;
+    if (!device->isEnabled()) {
+        ALOGW("Ignoring disabled device %s", device->getName().c_str());
+        return false;
+    }
+
+    std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
     // 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.");
+        ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
         return true;
     }
 
@@ -629,8 +572,9 @@
 
     dump += "Input Reader State:\n";
 
-    for (size_t i = 0; i < mDevices.size(); i++) {
-        mDevices.valueAt(i)->dump(dump);
+    for (const auto& devicePair : mDevices) {
+        InputDevice* const device = devicePair.second;
+        device->dump(dump);
     }
 
     dump += INDENT "Configuration:\n";
diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp
index 9f73680..a897141 100644
--- a/services/inputflinger/reader/InputReaderFactory.cpp
+++ b/services/inputflinger/reader/InputReaderFactory.cpp
@@ -22,7 +22,7 @@
 
 sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
                                            const sp<InputListenerInterface>& listener) {
-    return new InputReader(new EventHub(), policy, listener);
+    return new InputReader(std::make_unique<EventHub>(), policy, listener);
 }
 
 } // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 794396a..c17f3a1 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -145,12 +145,11 @@
  * which keys are currently down.  Finally, the event hub keeps track of the capabilities of
  * individual input devices, such as their class and the set of key codes that they support.
  */
-class EventHubInterface : public virtual RefBase {
-protected:
+class EventHubInterface {
+public:
     EventHubInterface() {}
     virtual ~EventHubInterface() {}
 
-public:
     // Synthetic raw event type codes produced when devices are added or removed.
     enum {
         // Sent when a device is added.
@@ -261,62 +260,64 @@
 public:
     EventHub();
 
-    virtual uint32_t getDeviceClasses(int32_t deviceId) const;
+    virtual uint32_t getDeviceClasses(int32_t deviceId) const override;
 
-    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const;
+    virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override;
 
-    virtual int32_t getDeviceControllerNumber(int32_t deviceId) const;
+    virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override;
 
-    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const;
+    virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override;
 
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
-                                         RawAbsoluteAxisInfo* outAxisInfo) const;
+                                         RawAbsoluteAxisInfo* outAxisInfo) const override;
 
-    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const;
+    virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override;
 
-    virtual bool hasInputProperty(int32_t deviceId, int property) const;
+    virtual bool hasInputProperty(int32_t deviceId, int property) const override;
 
     virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
                             int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
-                            uint32_t* outFlags) const;
+                            uint32_t* outFlags) const override;
 
-    virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const;
+    virtual status_t mapAxis(int32_t deviceId, int32_t scanCode,
+                             AxisInfo* outAxisInfo) const override;
 
-    virtual void setExcludedDevices(const std::vector<std::string>& devices);
+    virtual void setExcludedDevices(const std::vector<std::string>& devices) override;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const;
-    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const;
-    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const;
-    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const;
+    virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override;
+    virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override;
+    virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override;
+    virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
+                                          int32_t* outValue) const override;
 
     virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes,
-                                       uint8_t* outFlags) const;
+                                       uint8_t* outFlags) const override;
 
-    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize);
-    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId);
+    virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override;
+    virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override;
 
-    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const;
-    virtual bool hasLed(int32_t deviceId, int32_t led) const;
-    virtual void setLedState(int32_t deviceId, int32_t led, bool on);
+    virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override;
+    virtual bool hasLed(int32_t deviceId, int32_t led) const override;
+    virtual void setLedState(int32_t deviceId, int32_t led, bool on) override;
 
-    virtual void getVirtualKeyDefinitions(int32_t deviceId,
-                                          std::vector<VirtualKeyDefinition>& outVirtualKeys) const;
+    virtual void getVirtualKeyDefinitions(
+            int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override;
 
-    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
-    virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
+    virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override;
+    virtual bool setKeyboardLayoutOverlay(int32_t deviceId,
+                                          const sp<KeyCharacterMap>& map) override;
 
-    virtual void vibrate(int32_t deviceId, nsecs_t duration);
-    virtual void cancelVibrate(int32_t deviceId);
+    virtual void vibrate(int32_t deviceId, nsecs_t duration) override;
+    virtual void cancelVibrate(int32_t deviceId) override;
 
-    virtual void requestReopenDevices();
+    virtual void requestReopenDevices() override;
 
-    virtual void wake();
+    virtual void wake() override;
 
-    virtual void dump(std::string& dump);
-    virtual void monitor();
+    virtual void dump(std::string& dump) override;
+    virtual void monitor() override;
 
-protected:
-    virtual ~EventHub();
+    virtual ~EventHub() override;
 
 private:
     struct Device {
@@ -385,9 +386,9 @@
 
     void configureFd(Device* device);
 
-    bool isDeviceEnabled(int32_t deviceId);
-    status_t enableDevice(int32_t deviceId);
-    status_t disableDevice(int32_t deviceId);
+    bool isDeviceEnabled(int32_t deviceId) override;
+    status_t enableDevice(int32_t deviceId) override;
+    status_t disableDevice(int32_t deviceId) override;
     status_t registerFdForEpoll(int fd);
     status_t unregisterFdFromEpoll(int fd);
     status_t registerDeviceForEpollLocked(Device* device);
@@ -475,8 +476,6 @@
     size_t mPendingEventCount;
     size_t mPendingEventIndex;
     bool mPendingINotify;
-
-    bool mUsingEpollWakeup;
 };
 
 }; // namespace android
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 57f0b31..d06cc20 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -55,7 +55,9 @@
     inline std::optional<uint8_t> getAssociatedDisplayPort() const {
         return mAssociatedDisplayPort;
     }
-
+    inline std::optional<DisplayViewport> getAssociatedViewport() const {
+        return mAssociatedViewport;
+    }
     inline void setMic(bool hasMic) { mHasMic = hasMic; }
     inline bool hasMic() const { return mHasMic; }
 
@@ -65,7 +67,7 @@
     void setEnabled(bool enabled, nsecs_t when);
 
     void dump(std::string& dump);
-    void addMapper(InputMapper* mapper);
+    void populateMappers();
     void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
     void reset(nsecs_t when);
     void process(const RawEvent* rawEvents, size_t count);
@@ -112,7 +114,15 @@
         return value;
     }
 
-    std::optional<int32_t> getAssociatedDisplay();
+    std::optional<int32_t> getAssociatedDisplayId();
+
+    // construct and add a mapper to the input device
+    template <class T, typename... Args>
+    T& addMapper(Args... args) {
+        T* mapper = new T(this, args...);
+        mMappers.emplace_back(mapper);
+        return *mapper;
+    }
 
 private:
     InputReaderContext* mContext;
@@ -123,11 +133,12 @@
     std::string mAlias;
     uint32_t mClasses;
 
-    std::vector<InputMapper*> mMappers;
+    std::vector<std::unique_ptr<InputMapper>> mMappers;
 
     uint32_t mSources;
     bool mIsExternal;
     std::optional<uint8_t> mAssociatedDisplayPort;
+    std::optional<DisplayViewport> mAssociatedViewport;
     bool mHasMic;
     bool mDropUntilNextSync;
 
@@ -135,6 +146,26 @@
     int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
 
     PropertyMap mConfiguration;
+
+    // run a function against every mapper
+    inline void for_each_mapper(std::function<void(InputMapper&)> f) {
+        for (auto& mapperPtr : mMappers) {
+            f(*mapperPtr);
+        }
+    }
+
+    // return the first value returned by a function over every mapper.
+    // if all mappers return nullopt, return nullopt.
+    template <typename T>
+    inline std::optional<T> first_in_mappers(std::function<std::optional<T>(InputMapper&)> f) {
+        for (auto& mapperPtr : mMappers) {
+            std::optional<T> ret = f(*mapperPtr);
+            if (ret) {
+                return ret;
+            }
+        }
+        return std::nullopt;
+    }
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index e29c8f2..02957cd 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -21,13 +21,12 @@
 #include "InputListener.h"
 #include "InputReaderBase.h"
 #include "InputReaderContext.h"
+#include "InputThread.h"
 
-#include <utils/BitSet.h>
 #include <utils/Condition.h>
-#include <utils/KeyedVector.h>
 #include <utils/Mutex.h>
-#include <utils/Timers.h>
 
+#include <unordered_map>
 #include <vector>
 
 namespace android {
@@ -40,80 +39,94 @@
  * that it sends to the input listener.  Some functions of the input reader, such as early
  * event filtering in low power states, are controlled by a separate policy object.
  *
- * The InputReader owns a collection of InputMappers.  Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
+ * most of the work happens, but the InputReader can receive queries from other system
  * components running on arbitrary threads.  To keep things manageable, the InputReader
  * uses a single Mutex to guard its state.  The Mutex may be held while calling into the
  * EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
+ * InputListener. All calls to InputListener must happen from InputReader's thread.
  */
 class InputReader : public InputReaderInterface {
 public:
-    InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy,
+    InputReader(std::shared_ptr<EventHubInterface> eventHub,
+                const sp<InputReaderPolicyInterface>& policy,
                 const sp<InputListenerInterface>& listener);
     virtual ~InputReader();
 
-    virtual void dump(std::string& dump);
-    virtual void monitor();
+    virtual void dump(std::string& dump) override;
+    virtual void monitor() override;
 
-    virtual void loopOnce();
+    virtual status_t start() override;
+    virtual status_t stop() override;
 
-    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices);
+    virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
 
-    virtual bool isInputDeviceEnabled(int32_t deviceId);
+    virtual bool isInputDeviceEnabled(int32_t deviceId) override;
 
-    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode);
-    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw);
+    virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask,
+                                     int32_t scanCode) override;
+    virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask,
+                                    int32_t keyCode) override;
+    virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
 
-    virtual void toggleCapsLockState(int32_t deviceId);
+    virtual void toggleCapsLockState(int32_t deviceId) override;
 
     virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes,
-                         const int32_t* keyCodes, uint8_t* outFlags);
+                         const int32_t* keyCodes, uint8_t* outFlags) override;
 
-    virtual void requestRefreshConfiguration(uint32_t changes);
+    virtual void requestRefreshConfiguration(uint32_t changes) override;
 
     virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize,
-                         ssize_t repeat, int32_t token);
-    virtual void cancelVibrate(int32_t deviceId, int32_t token);
+                         ssize_t repeat, int32_t token) override;
+    virtual void cancelVibrate(int32_t deviceId, int32_t token) override;
 
-    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId);
+    virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+
 protected:
     // These members are protected so they can be instrumented by test cases.
     virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
                                             const InputDeviceIdentifier& identifier,
                                             uint32_t classes);
 
+    // With each iteration of the loop, InputReader reads and processes one incoming message from
+    // the EventHub.
+    void loopOnce();
+
     class ContextImpl : public InputReaderContext {
         InputReader* mReader;
 
     public:
         explicit ContextImpl(InputReader* reader);
 
-        virtual void updateGlobalMetaState();
-        virtual int32_t getGlobalMetaState();
-        virtual void disableVirtualKeysUntil(nsecs_t time);
+        virtual void updateGlobalMetaState() override;
+        virtual int32_t getGlobalMetaState() override;
+        virtual void disableVirtualKeysUntil(nsecs_t time) override;
         virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode,
-                                          int32_t scanCode);
-        virtual void fadePointer();
-        virtual void requestTimeoutAtTime(nsecs_t when);
-        virtual int32_t bumpGeneration();
-        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices);
-        virtual void dispatchExternalStylusState(const StylusState& outState);
-        virtual InputReaderPolicyInterface* getPolicy();
-        virtual InputListenerInterface* getListener();
-        virtual EventHubInterface* getEventHub();
-        virtual uint32_t getNextSequenceNum();
+                                          int32_t scanCode) override;
+        virtual void fadePointer() override;
+        virtual void requestTimeoutAtTime(nsecs_t when) override;
+        virtual int32_t bumpGeneration() override;
+        virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override;
+        virtual void dispatchExternalStylusState(const StylusState& outState) override;
+        virtual InputReaderPolicyInterface* getPolicy() override;
+        virtual InputListenerInterface* getListener() override;
+        virtual EventHubInterface* getEventHub() override;
+        virtual uint32_t getNextSequenceNum() override;
     } mContext;
 
     friend class ContextImpl;
 
 private:
+    std::unique_ptr<InputThread> mThread;
+
     Mutex mLock;
 
     Condition mReaderIsAliveCondition;
 
-    sp<EventHubInterface> mEventHub;
+    // This could be unique_ptr, but due to the way InputReader tests are written,
+    // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test
+    // in parallel to passing it to the InputReader.
+    std::shared_ptr<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
     sp<QueuedInputListener> mQueuedListener;
 
@@ -126,7 +139,7 @@
     static const int EVENT_BUFFER_SIZE = 256;
     RawEvent mEventBuffer[EVENT_BUFFER_SIZE];
 
-    KeyedVector<int32_t, InputDevice*> mDevices;
+    std::unordered_map<int32_t /*deviceId*/, InputDevice*> mDevices;
 
     // low-level input event decoding and device management
     void processEventsLocked(const RawEvent* rawEvents, size_t count);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index da85fda..69a75ba 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -189,12 +189,32 @@
 
         // Update the PointerController if viewports changed.
         if (mParameters.mode == Parameters::MODE_POINTER) {
-            getPolicy()->obtainPointerController(getDeviceId());
+            updatePointerControllerDisplayViewport(*config);
         }
         bumpGeneration();
     }
 }
 
+void CursorInputMapper::updatePointerControllerDisplayViewport(
+        const InputReaderConfiguration& config) {
+    std::optional<DisplayViewport> viewport =
+            config.getDisplayViewportById(config.defaultPointerDisplayId);
+    if (!viewport) {
+        ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
+              "mapper. Fall back to default display",
+              config.defaultPointerDisplayId);
+        viewport = config.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
+    }
+
+    if (!viewport) {
+        ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
+              " PointerController.");
+        return;
+    }
+
+    mPointerController->setDisplayViewport(*viewport);
+}
+
 void CursorInputMapper::configureParameters() {
     mParameters.mode = Parameters::MODE_POINTER;
     String8 cursorModeString;
@@ -313,6 +333,8 @@
     mPointerVelocityControl.move(when, &deltaX, &deltaY);
 
     int32_t displayId;
+    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
     if (mSource == AINPUT_SOURCE_MOUSE) {
         if (moved || scrolled || buttonsChanged) {
             mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
@@ -328,10 +350,9 @@
             mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
         }
 
-        float x, y;
-        mPointerController->getPosition(&x, &y);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
         pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
         displayId = mPointerController->getDisplayId();
@@ -378,9 +399,9 @@
                                              mSource, displayId, policyFlags,
                                              AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
                                              metaState, buttonState, MotionClassification::NONE,
-                                             AMOTION_EVENT_EDGE_FLAG_NONE,
-                                             /* deviceTimestamp */ 0, 1, &pointerProperties,
-                                             &pointerCoords, mXPrecision, mYPrecision, downTime,
+                                             AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                             &pointerCoords, mXPrecision, mYPrecision,
+                                             xCursorPosition, yCursorPosition, downTime,
                                              /* videoFrames */ {});
                 getListener()->notifyMotion(&releaseArgs);
             }
@@ -389,9 +410,9 @@
         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 */ {});
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+                              mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
+                              /* videoFrames */ {});
         getListener()->notifyMotion(&args);
 
         if (buttonsPressed) {
@@ -403,9 +424,9 @@
                                            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,
+                                           AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                           &pointerCoords, mXPrecision, mYPrecision,
+                                           xCursorPosition, yCursorPosition, downTime,
                                            /* videoFrames */ {});
                 getListener()->notifyMotion(&pressArgs);
             }
@@ -418,10 +439,9 @@
             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 */ {});
+                                       AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                       &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                       yCursorPosition, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&hoverArgs);
         }
 
@@ -434,10 +454,9 @@
                                         mSource, displayId, policyFlags,
                                         AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
                                         currentButtonState, MotionClassification::NONE,
-                                        AMOTION_EVENT_EDGE_FLAG_NONE,
-                                        /* deviceTimestamp */ 0, 1, &pointerProperties,
-                                        &pointerCoords, mXPrecision, mYPrecision, downTime,
-                                        /* videoFrames */ {});
+                                        AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                        &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
+                                        yCursorPosition, downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&scrollArgs);
         }
     }
@@ -464,7 +483,7 @@
     }
 }
 
-std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() {
+std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
         if (mParameters.mode == Parameters::MODE_POINTER) {
             return std::make_optional(mPointerController->getDisplayId());
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index eb2ad54..d56f9be 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -56,18 +56,19 @@
     explicit CursorInputMapper(InputDevice* device);
     virtual ~CursorInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
 
-    virtual void fadePointer();
+    virtual void fadePointer() override;
 
-    virtual std::optional<int32_t> getAssociatedDisplay();
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
 
 private:
     // Amount that trackball needs to move in order to generate a key event.
@@ -116,8 +117,9 @@
     void dumpParameters(std::string& dump);
 
     void sync(nsecs_t when);
+    void updatePointerControllerDisplayViewport(const InputReaderConfiguration& config);
 };
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
\ No newline at end of file
+#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 9764fbb..34f339a 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -30,13 +30,13 @@
     explicit ExternalStylusInputMapper(InputDevice* device);
     virtual ~ExternalStylusInputMapper() = default;
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
-    virtual void sync(nsecs_t when);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 private:
     SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
@@ -44,6 +44,8 @@
     TouchButtonAccumulator mTouchButtonAccumulator;
 
     StylusState mStylusState;
+
+    void sync(nsecs_t when);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index cfd207c..a559ef8 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -73,7 +73,7 @@
     virtual void updateExternalStylusState(const StylusState& state);
 
     virtual void fadePointer();
-    virtual std::optional<int32_t> getAssociatedDisplay() { return std::nullopt; }
+    virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
 
 protected:
     InputDevice* mDevice;
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index b493e83..50adf73 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -334,9 +334,10 @@
     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 */ {});
+                          MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                          &pointerProperties, &pointerCoords, 0, 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
     getListener()->notifyMotion(&args);
 }
 
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 1b071d0..b46d27d 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -26,12 +26,13 @@
     explicit JoystickInputMapper(InputDevice* device);
     virtual ~JoystickInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 private:
     struct Axis {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 0e91c0e..348a7ad 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -127,6 +127,22 @@
     dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
 }
 
+std::optional<DisplayViewport> KeyboardInputMapper::findViewport(
+        nsecs_t when, const InputReaderConfiguration* config) {
+    const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort();
+    if (displayPort) {
+        // Find the viewport that contains the same port
+        return mDevice->getAssociatedViewport();
+    }
+
+    // No associated display defined, try to find default display if orientationAware.
+    if (mParameters.orientationAware) {
+        return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    }
+
+    return std::nullopt;
+}
+
 void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
                                     uint32_t changes) {
     InputMapper::configure(when, config, changes);
@@ -137,9 +153,7 @@
     }
 
     if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
-        if (mParameters.orientationAware) {
-            mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
-        }
+        mViewport = findViewport(when, config);
     }
 }
 
@@ -169,6 +183,9 @@
 
     mParameters.handlesKeyRepeat = false;
     config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+
+    mParameters.doNotWakeByDefault = false;
+    config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault);
 }
 
 void KeyboardInputMapper::dumpParameters(std::string& dump) {
@@ -317,10 +334,12 @@
 
     // Key down on external an keyboard should wake the device.
     // We don't do this for internal keyboards to prevent them from waking up in your pocket.
-    // For internal keyboards, the key layout file should specify the policy flags for
-    // each wake key individually.
+    // For internal keyboards and devices for which the default wake behavior is explicitly
+    // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
+    // wake key individually.
     // TODO: Use the input device configuration to control this behavior more finely.
-    if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
+    if (down && getDevice()->isExternal() && !mParameters.doNotWakeByDefault &&
+        !isMediaKey(keyCode)) {
         policyFlags |= POLICY_FLAG_WAKE;
     }
 
@@ -409,4 +428,11 @@
     }
 }
 
+std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
+    if (mViewport) {
+        return std::make_optional(mViewport->displayId);
+    }
+    return std::nullopt;
+}
+
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 7a68fc3..badbcb2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -26,20 +26,22 @@
     KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType);
     virtual ~KeyboardInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-                                       const int32_t* keyCodes, uint8_t* outFlags);
+                                       const int32_t* keyCodes, uint8_t* outFlags) override;
 
-    virtual int32_t getMetaState();
-    virtual void updateMetaState(int32_t keyCode);
+    virtual int32_t getMetaState() override;
+    virtual void updateMetaState(int32_t keyCode) override;
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
 
 private:
     // The current viewport.
@@ -71,6 +73,7 @@
     struct Parameters {
         bool orientationAware;
         bool handlesKeyRepeat;
+        bool doNotWakeByDefault;
     } mParameters;
 
     void configureParameters();
@@ -92,6 +95,8 @@
     void initializeLedState(LedState& ledState, int32_t led);
     void updateLedState(bool reset);
     void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset);
+    std::optional<DisplayViewport> findViewport(nsecs_t when,
+                                                const InputReaderConfiguration* config);
 };
 
 } // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 7460a31..f42ddcf 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -72,7 +72,6 @@
     } else {
         clearSlots(-1);
     }
-    mDeviceTimestamp = 0;
 }
 
 void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) {
@@ -166,8 +165,6 @@
     } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
         // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
         mCurrentSlot += 1;
-    } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
-        mDeviceTimestamp = rawEvent->value;
     }
 }
 
@@ -212,6 +209,8 @@
                 return AMOTION_EVENT_TOOL_TYPE_FINGER;
             case MT_TOOL_PEN:
                 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
+            case MT_TOOL_PALM:
+                return AMOTION_EVENT_TOOL_TYPE_PALM;
         }
     }
     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -250,6 +249,14 @@
             continue;
         }
 
+        if (inSlot->getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
+            if (!mCurrentMotionAborted) {
+                ALOGI("Canceling touch gesture from device %s because the palm event was detected",
+                      getDeviceName().c_str());
+                cancelTouch(when);
+            }
+        }
+
         if (outCount >= MAX_POINTERS) {
 #if DEBUG_POINTERS
             ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
@@ -316,7 +323,6 @@
         outCount += 1;
     }
 
-    outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp();
     outState->rawPointerData.pointerCount = outCount;
     mPointerIdBits = newPointerIdBits;
 
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 873dda1..a45c3cb 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -78,7 +78,6 @@
 
     inline size_t getSlotCount() const { return mSlotCount; }
     inline const Slot* getSlot(size_t index) const { return &mSlots[index]; }
-    inline uint32_t getDeviceTimestamp() const { return mDeviceTimestamp; }
 
 private:
     int32_t mCurrentSlot;
@@ -86,7 +85,6 @@
     size_t mSlotCount;
     bool mUsingSlotsProtocol;
     bool mHaveStylus;
-    uint32_t mDeviceTimestamp;
 
     void clearSlots(int32_t initialSlot);
 };
@@ -96,8 +94,8 @@
     explicit MultiTouchInputMapper(InputDevice* device);
     virtual ~MultiTouchInputMapper();
 
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 protected:
     virtual void syncTouch(nsecs_t when, RawState* outState);
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 803fdf3..e113cca 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -122,9 +122,9 @@
         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 */ {});
+                                    AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
+                                    &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                    AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {});
         getListener()->notifyMotion(&scrollArgs);
     }
 
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 2648837..38c7258 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -27,12 +27,13 @@
     explicit RotaryEncoderInputMapper(InputDevice* device);
     virtual ~RotaryEncoderInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 private:
     CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index d6b1455..8438eee 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -27,8 +27,8 @@
     explicit SingleTouchInputMapper(InputDevice* device);
     virtual ~SingleTouchInputMapper();
 
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
 protected:
     virtual void syncTouch(nsecs_t when, RawState* outState);
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4ff941f..16095b9 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -56,8 +56,8 @@
 void SwitchInputMapper::sync(nsecs_t when) {
     if (mUpdatedSwitchMask) {
         uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
-        NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
-                              mUpdatedSwitchMask);
+        NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0 /*policyFlags*/,
+                              updatedSwitchValues, mUpdatedSwitchMask);
         getListener()->notifySwitch(&args);
 
         mUpdatedSwitchMask = 0;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index dd4bb9e..e65d4e2 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -26,11 +26,11 @@
     explicit SwitchInputMapper(InputDevice* device);
     virtual ~SwitchInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode);
-    virtual void dump(std::string& dump);
+    virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
+    virtual void dump(std::string& dump) override;
 
 private:
     uint32_t mSwitchValues;
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 32ed97b..3b20173 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -23,11 +23,6 @@
 #include "TouchButtonAccumulator.h"
 #include "TouchCursorInputMapperCommon.h"
 
-#include <statslog.h>
-
-// How often to report input event statistics
-static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60);
-
 namespace android {
 
 // --- Constants ---
@@ -557,24 +552,31 @@
  * 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
+ * 2. Always use the suggested viewport from WindowManagerService for pointers.
+ * 3. 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.
+ * 4. 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;
+            return mDevice->getAssociatedViewport();
         }
 
+        if (mDeviceMode == DEVICE_MODE_POINTER) {
+            std::optional<DisplayViewport> viewport =
+                    mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
+            if (viewport) {
+                return viewport;
+            } else {
+                ALOGW("Can't find designated display viewport with ID %" PRId32 " for pointers.",
+                      mConfig.defaultPointerDisplayId);
+            }
+        }
+
+        // Check if uniqueDisplayId is specified in idc file.
         if (!mParameters.uniqueDisplayId.empty()) {
             return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId);
         }
@@ -598,6 +600,7 @@
         return viewport;
     }
 
+    // No associated display, return a non-display viewport.
     DisplayViewport newViewport;
     // Raw width and height in the natural orientation.
     int32_t rawWidth = mRawPointerAxes.getRawWidth();
@@ -677,7 +680,7 @@
                     naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
                     naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
-                    naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
+                    naturalPhysicalLeft = mViewport.deviceHeight - naturalPhysicalWidth;
                     naturalPhysicalTop = mViewport.physicalLeft;
                     naturalDeviceWidth = mViewport.deviceHeight;
                     naturalDeviceHeight = mViewport.deviceWidth;
@@ -698,7 +701,7 @@
                     naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
                     naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
                     naturalPhysicalLeft = mViewport.physicalTop;
-                    naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight;
+                    naturalPhysicalTop = mViewport.deviceWidth - naturalPhysicalHeight;
                     naturalDeviceWidth = mViewport.deviceHeight;
                     naturalDeviceHeight = mViewport.deviceWidth;
                     break;
@@ -758,6 +761,11 @@
         (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
         if (mPointerController == nullptr || viewportChanged) {
             mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+            // Set the DisplayViewport for the PointerController to the default pointer display
+            // that is recommended by the configuration before using it.
+            std::optional<DisplayViewport> defaultViewport =
+                    mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
+            mPointerController->setDisplayViewport(defaultViewport.value_or(mViewport));
         }
     } else {
         mPointerController.clear();
@@ -1395,26 +1403,12 @@
     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.count);
-        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);
     }
 }
@@ -1848,7 +1842,6 @@
         int32_t buttonState = mCurrentCookedState.buttonState;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
                        buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                       mCurrentCookedState.deviceTimestamp,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1869,7 +1862,6 @@
             // The listener takes care of batching moves so we don't have to deal with that here.
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
                            buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                           mCurrentCookedState.deviceTimestamp,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1,
@@ -1901,7 +1893,7 @@
             uint32_t upId = upIdBits.clearFirstMarkedBit();
 
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
-                           metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                           metaState, buttonState, 0,
                            mLastCookedState.cookedPointerData.pointerProperties,
                            mLastCookedState.cookedPointerData.pointerCoords,
                            mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId,
@@ -1915,8 +1907,7 @@
         if (moveNeeded && !moveIdBits.isEmpty()) {
             ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                           buttonState, 0, mCurrentCookedState.deviceTimestamp,
-                           mCurrentCookedState.cookedPointerData.pointerProperties,
+                           buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1,
                            mOrientedXPrecision, mOrientedYPrecision, mDownTime);
@@ -1933,7 +1924,7 @@
             }
 
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
-                           metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                           metaState, buttonState, 0,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits,
@@ -1948,7 +1939,7 @@
          !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) {
         int32_t metaState = getContext()->getGlobalMetaState();
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState,
-                       mLastCookedState.buttonState, 0, mLastCookedState.deviceTimestamp,
+                       mLastCookedState.buttonState, 0,
                        mLastCookedState.cookedPointerData.pointerProperties,
                        mLastCookedState.cookedPointerData.pointerCoords,
                        mLastCookedState.cookedPointerData.idToIndex,
@@ -1965,7 +1956,6 @@
         if (!mSentHoverEnter) {
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
                            metaState, mCurrentRawState.buttonState, 0,
-                           mCurrentCookedState.deviceTimestamp,
                            mCurrentCookedState.cookedPointerData.pointerProperties,
                            mCurrentCookedState.cookedPointerData.pointerCoords,
                            mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1975,7 +1965,7 @@
         }
 
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                       mCurrentRawState.buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                       mCurrentRawState.buttonState, 0,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex,
@@ -1994,7 +1984,6 @@
         buttonState &= ~actionButton;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
                        actionButton, 0, metaState, buttonState, 0,
-                       mCurrentCookedState.deviceTimestamp,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -2011,7 +2000,7 @@
         int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit());
         buttonState |= actionButton;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton,
-                       0, metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp,
+                       0, metaState, buttonState, 0,
                        mCurrentCookedState.cookedPointerData.pointerProperties,
                        mCurrentCookedState.cookedPointerData.pointerCoords,
                        mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1,
@@ -2030,7 +2019,6 @@
     uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;
 
     mCurrentCookedState.cookedPointerData.clear();
-    mCurrentCookedState.deviceTimestamp = mCurrentRawState.deviceTimestamp;
     mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount;
     mCurrentCookedState.cookedPointerData.hoveringIdBits =
             mCurrentRawState.rawPointerData.hoveringIdBits;
@@ -2202,6 +2190,7 @@
         // TODO: Adjust coverage coords?
         float xTransformed = in.x, yTransformed = in.y;
         mAffineTransform.applyTo(xTransformed, yTransformed);
+        rotateAndScale(xTransformed, yTransformed);
 
         // Adjust X, Y, and coverage coords for surface orientation.
         float x, y;
@@ -2209,8 +2198,8 @@
 
         switch (mSurfaceOrientation) {
             case DISPLAY_ORIENTATION_90:
-                x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
-                y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
+                x = yTransformed + mYTranslate;
+                y = xTransformed + mXTranslate;
                 left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
                 right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
                 bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
@@ -2223,8 +2212,8 @@
                 }
                 break;
             case DISPLAY_ORIENTATION_180:
-                x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale;
-                y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate;
+                x = xTransformed + mXTranslate;
+                y = yTransformed + mYTranslate;
                 left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
                 right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
                 bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
@@ -2237,8 +2226,8 @@
                 }
                 break;
             case DISPLAY_ORIENTATION_270:
-                x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale;
-                y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+                x = yTransformed + mYTranslate;
+                y = xTransformed + mXTranslate;
                 left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
                 right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
                 bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
@@ -2251,8 +2240,8 @@
                 }
                 break;
             default:
-                x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
-                y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+                x = xTransformed + mXTranslate;
+                y = yTransformed + mYTranslate;
                 left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
                 right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
                 bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
@@ -2428,7 +2417,7 @@
     if (!dispatchedGestureIdBits.isEmpty()) {
         if (cancelPreviousGesture) {
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
-                           buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                           buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                            mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                            mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
                            mPointerGesture.downTime);
@@ -2447,7 +2436,7 @@
 
                 dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0,
                                metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
-                               /* deviceTimestamp */ 0, mPointerGesture.lastGestureProperties,
+                               mPointerGesture.lastGestureProperties,
                                mPointerGesture.lastGestureCoords,
                                mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0,
                                0, mPointerGesture.downTime);
@@ -2460,7 +2449,7 @@
     // Send motion events for all pointers that moved.
     if (moveNeeded) {
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0,
@@ -2480,8 +2469,7 @@
             }
 
             dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0,
-                           metaState, buttonState, 0,
-                           /* deviceTimestamp */ 0, mPointerGesture.currentGestureProperties,
+                           metaState, buttonState, 0, mPointerGesture.currentGestureProperties,
                            mPointerGesture.currentGestureCoords,
                            mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0,
                            0, mPointerGesture.downTime);
@@ -2491,7 +2479,7 @@
     // Send motion events for hover.
     if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) {
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.currentGestureProperties,
                        mPointerGesture.currentGestureCoords,
                        mPointerGesture.currentGestureIdToIndex,
@@ -2518,9 +2506,8 @@
         NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
                               displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                               metaState, buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE,
-                              /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0,
-                              mPointerGesture.downTime, /* videoFrames */ {});
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
+                              0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -2548,7 +2535,7 @@
         int32_t metaState = getContext()->getGlobalMetaState();
         int32_t buttonState = mCurrentRawState.buttonState;
         dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState,
-                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0,
+                       buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
                        mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords,
                        mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1,
                        0, 0, mPointerGesture.downTime);
@@ -3418,17 +3405,19 @@
     int32_t metaState = getContext()->getGlobalMetaState();
     int32_t displayId = mViewport.displayId;
 
-    if (mPointerController != nullptr) {
-        if (down || hovering) {
-            mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
-            mPointerController->clearSpots();
-            mPointerController->setButtonState(mCurrentRawState.buttonState);
-            mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
-        } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
-            mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
-        }
-        displayId = mPointerController->getDisplayId();
+    if (down || hovering) {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+        mPointerController->clearSpots();
+        mPointerController->setButtonState(mCurrentRawState.buttonState);
+        mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE);
+    } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
+        mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
     }
+    displayId = mPointerController->getDisplayId();
+
+    float xCursorPosition;
+    float yCursorPosition;
+    mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
 
     if (mPointerSimple.down && !down) {
         mPointerSimple.down = false;
@@ -3437,9 +3426,9 @@
         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,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
@@ -3451,9 +3440,9 @@
         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,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties,
+                              &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
@@ -3467,11 +3456,10 @@
             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,
-                                  /* videoFrames */ {});
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
@@ -3479,10 +3467,10 @@
         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,
-                              /* videoFrames */ {});
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -3494,11 +3482,10 @@
             NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
                                   displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0,
                                   metaState, mCurrentRawState.buttonState,
-                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
-                                  /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties,
-                                  &mPointerSimple.currentCoords, mOrientedXPrecision,
-                                  mOrientedYPrecision, mPointerSimple.downTime,
-                                  /* videoFrames */ {});
+                                  MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+                                  &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
+                                  mOrientedXPrecision, mOrientedYPrecision, xCursorPosition,
+                                  yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {});
             getListener()->notifyMotion(&args);
         }
 
@@ -3506,10 +3493,10 @@
         NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource,
                               displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0,
                               metaState, mCurrentRawState.buttonState, MotionClassification::NONE,
-                              AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1,
-                              &mPointerSimple.currentProperties, &mPointerSimple.currentCoords,
-                              mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime,
-                              /* videoFrames */ {});
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &mPointerSimple.currentCoords, mOrientedXPrecision,
+                              mOrientedYPrecision, xCursorPosition, yCursorPosition,
+                              mPointerSimple.downTime, /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
 
@@ -3528,9 +3515,9 @@
         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,
+                              AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties,
+                              &pointerCoords, mOrientedXPrecision, mOrientedYPrecision,
+                              xCursorPosition, yCursorPosition, mPointerSimple.downTime,
                               /* videoFrames */ {});
         getListener()->notifyMotion(&args);
     }
@@ -3554,7 +3541,7 @@
 void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
                                       int32_t action, int32_t actionButton, int32_t flags,
                                       int32_t metaState, int32_t buttonState, int32_t edgeFlags,
-                                      uint32_t deviceTimestamp, const PointerProperties* properties,
+                                      const PointerProperties* properties,
                                       const PointerCoords* coords, const uint32_t* idToIndex,
                                       BitSet32 idBits, int32_t changedId, float xPrecision,
                                       float yPrecision, nsecs_t downTime) {
@@ -3589,16 +3576,21 @@
             ALOG_ASSERT(false);
         }
     }
-    const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
+    float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
+        mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
+    }
+    const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
     const int32_t deviceId = getDeviceId();
     std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
     std::for_each(frames.begin(), frames.end(),
                   [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
     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));
+                          MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
+                          pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition,
+                          downTime, std::move(frames));
     getListener()->notifyMotion(&args);
 }
 
@@ -3643,13 +3635,34 @@
     abortTouches(when, 0 /* policyFlags*/);
 }
 
+void TouchInputMapper::rotateAndScale(float& x, float& y) {
+    switch (mSurfaceOrientation) {
+        case DISPLAY_ORIENTATION_90:
+            x = float(mRawPointerAxes.x.maxValue - x) * mXScale;
+            y = float(y - mRawPointerAxes.y.minValue) * mYScale;
+            break;
+        case DISPLAY_ORIENTATION_180:
+            x = float(mRawPointerAxes.x.maxValue - x) * mXScale;
+            y = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+            break;
+        case DISPLAY_ORIENTATION_270:
+            x = float(x - mRawPointerAxes.x.minValue) * mXScale;
+            y = float(mRawPointerAxes.y.maxValue - y) * mYScale;
+            break;
+        default:
+            x = float(x - mRawPointerAxes.x.minValue) * mXScale;
+            y = float(y - mRawPointerAxes.y.minValue) * mYScale;
+            break;
+    }
+}
+
 bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
-    const float scaledX = x * mXScale;
-    const float scaledY = y * mYScale;
+    float xTransformed = x, yTransformed = y;
+    rotateAndScale(xTransformed, yTransformed);
     return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
-            scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth &&
+            xTransformed >= mSurfaceLeft && xTransformed <= mSurfaceLeft + mSurfaceWidth &&
             y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
-            scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight;
+            yTransformed >= mSurfaceTop && yTransformed <= mSurfaceTop + mSurfaceHeight;
 }
 
 const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
@@ -3895,7 +3908,7 @@
     return true;
 }
 
-std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() {
+std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
     if (mParameters.hasAssociatedDisplay) {
         if (mDeviceMode == DEVICE_MODE_POINTER) {
             return std::make_optional(mPointerController->getDisplayId());
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index d14812a..4b1c0cb 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -28,68 +28,6 @@
 
 namespace android {
 
-/**
- * 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;
-    }
-};
-
 /* Raw axis information from the driver. */
 struct RawPointerAxes {
     RawAbsoluteAxisInfo x;
@@ -197,23 +135,24 @@
     explicit TouchInputMapper(InputDevice* device);
     virtual ~TouchInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void dump(std::string& dump);
-    virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes);
-    virtual void reset(nsecs_t when);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void dump(std::string& dump) override;
+    virtual void configure(nsecs_t when, const InputReaderConfiguration* config,
+                           uint32_t changes) override;
+    virtual void reset(nsecs_t when) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
-    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
+    virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
+    virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
     virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes,
-                                       const int32_t* keyCodes, uint8_t* outFlags);
+                                       const int32_t* keyCodes, uint8_t* outFlags) override;
 
-    virtual void fadePointer();
-    virtual void cancelTouch(nsecs_t when);
-    virtual void timeoutExpired(nsecs_t when);
-    virtual void updateExternalStylusState(const StylusState& state);
-    virtual std::optional<int32_t> getAssociatedDisplay();
+    virtual void fadePointer() override;
+    virtual void cancelTouch(nsecs_t when) override;
+    virtual void timeoutExpired(nsecs_t when) override;
+    virtual void updateExternalStylusState(const StylusState& state) override;
+    virtual std::optional<int32_t> getAssociatedDisplayId() override;
 
 protected:
     CursorButtonAccumulator mCursorButtonAccumulator;
@@ -358,7 +297,6 @@
 
     struct RawState {
         nsecs_t when;
-        uint32_t deviceTimestamp;
 
         // Raw pointer sample data.
         RawPointerData rawPointerData;
@@ -371,7 +309,6 @@
 
         void copyFrom(const RawState& other) {
             when = other.when;
-            deviceTimestamp = other.deviceTimestamp;
             rawPointerData.copyFrom(other.rawPointerData);
             buttonState = other.buttonState;
             rawVScroll = other.rawVScroll;
@@ -380,7 +317,6 @@
 
         void clear() {
             when = 0;
-            deviceTimestamp = 0;
             rawPointerData.clear();
             buttonState = 0;
             rawVScroll = 0;
@@ -389,7 +325,6 @@
     };
 
     struct CookedState {
-        uint32_t deviceTimestamp;
         // Cooked pointer sample data.
         CookedPointerData cookedPointerData;
 
@@ -401,7 +336,6 @@
         int32_t buttonState;
 
         void copyFrom(const CookedState& other) {
-            deviceTimestamp = other.deviceTimestamp;
             cookedPointerData.copyFrom(other.cookedPointerData);
             fingerIdBits = other.fingerIdBits;
             stylusIdBits = other.stylusIdBits;
@@ -410,7 +344,6 @@
         }
 
         void clear() {
-            deviceTimestamp = 0;
             cookedPointerData.clear();
             fingerIdBits.clear();
             stylusIdBits.clear();
@@ -759,9 +692,6 @@
     VelocityControl mWheelXVelocityControl;
     VelocityControl mWheelYVelocityControl;
 
-    // Latency statistics for touch events
-    struct LatencyStatistics mStatistics;
-
     std::optional<DisplayViewport> findViewport();
 
     void resetExternalStylus();
@@ -811,10 +741,9 @@
     // it is the first / last pointer to go down / up.
     void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action,
                         int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
-                        int32_t edgeFlags, uint32_t deviceTimestamp,
-                        const PointerProperties* properties, const PointerCoords* coords,
-                        const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId,
-                        float xPrecision, float yPrecision, nsecs_t downTime);
+                        int32_t edgeFlags, const PointerProperties* properties,
+                        const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits,
+                        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime);
 
     // Updates pointer coords and properties for pointers with specified ids that have moved.
     // Returns true if any of them changed.
@@ -828,11 +757,10 @@
 
     static void assignPointerIds(const RawState* last, RawState* current);
 
-    void reportEventForStatistics(nsecs_t evdevTime);
-
     const char* modeToString(DeviceMode deviceMode);
+    void rotateAndScale(float& x, float& y);
 };
 
 } // namespace android
 
-#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
+#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 6b33f48..dc67890 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -26,14 +26,15 @@
     explicit VibratorInputMapper(InputDevice* device);
     virtual ~VibratorInputMapper();
 
-    virtual uint32_t getSources();
-    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo);
-    virtual void process(const RawEvent* rawEvent);
+    virtual uint32_t getSources() override;
+    virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+    virtual void process(const RawEvent* rawEvent) override;
 
-    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token);
-    virtual void cancelVibrate(int32_t token);
-    virtual void timeoutExpired(nsecs_t when);
-    virtual void dump(std::string& dump);
+    virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat,
+                         int32_t token) override;
+    virtual void cancelVibrate(int32_t token) override;
+    virtual void timeoutExpired(nsecs_t when) override;
+    virtual void dump(std::string& dump) override;
 
 private:
     bool mVibrating;
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index c4f8626..f913d82 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -4,17 +4,20 @@
     name: "inputflinger_tests",
     srcs: [
         "BlockingQueue_test.cpp",
+        "EventHub_test.cpp",
         "TestInputListener.cpp",
         "InputClassifier_test.cpp",
         "InputClassifierConverter_test.cpp",
         "InputDispatcher_test.cpp",
         "InputReader_test.cpp",
+        "UinputDevice.cpp",
     ],
     cflags: [
         "-Wall",
         "-Werror",
         "-Wextra",
         "-Wno-unused-parameter",
+        "-Wthread-safety",
     ],
     shared_libs: [
         "android.hardware.input.classifier@1.0",
@@ -35,4 +38,5 @@
     header_libs: [
         "libinputreader_headers",
     ],
+    require_root: true,
 }
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
new file mode 100644
index 0000000..be2e19e
--- /dev/null
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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 "EventHub.h"
+
+#include "UinputDevice.h"
+
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <chrono>
+
+#define TAG "EventHub_test"
+
+using android::createUinputDevice;
+using android::EventHub;
+using android::EventHubInterface;
+using android::InputDeviceIdentifier;
+using android::RawEvent;
+using android::sp;
+using android::UinputHomeKey;
+using std::chrono_literals::operator""ms;
+
+static constexpr bool DEBUG = false;
+
+static void dumpEvents(const std::vector<RawEvent>& events) {
+    for (const RawEvent& event : events) {
+        if (event.type >= EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+            switch (event.type) {
+                case EventHubInterface::DEVICE_ADDED:
+                    ALOGI("Device added: %i", event.deviceId);
+                    break;
+                case EventHubInterface::DEVICE_REMOVED:
+                    ALOGI("Device removed: %i", event.deviceId);
+                    break;
+                case EventHubInterface::FINISHED_DEVICE_SCAN:
+                    ALOGI("Finished device scan.");
+                    break;
+            }
+        } else {
+            ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
+                  event.deviceId, event.when, event.type, event.code, event.value);
+        }
+    }
+}
+
+// --- EventHubTest ---
+class EventHubTest : public testing::Test {
+protected:
+    std::unique_ptr<EventHubInterface> mEventHub;
+    // We are only going to emulate a single input device currently.
+    std::unique_ptr<UinputHomeKey> mKeyboard;
+    int32_t mDeviceId;
+
+    virtual void SetUp() override {
+        mEventHub = std::make_unique<EventHub>();
+        consumeInitialDeviceAddedEvents();
+        mKeyboard = createUinputDevice<UinputHomeKey>();
+        mDeviceId = waitForDeviceCreation();
+    }
+    virtual void TearDown() override {
+        mKeyboard.reset();
+        waitForDeviceClose(mDeviceId);
+    }
+
+    /**
+     * Return the device id of the created device.
+     */
+    int32_t waitForDeviceCreation();
+    void waitForDeviceClose(int32_t deviceId);
+    void consumeInitialDeviceAddedEvents();
+    std::vector<RawEvent> getEvents(std::chrono::milliseconds timeout = 5ms);
+};
+
+std::vector<RawEvent> EventHubTest::getEvents(std::chrono::milliseconds timeout) {
+    static constexpr size_t EVENT_BUFFER_SIZE = 256;
+    std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
+    std::vector<RawEvent> events;
+
+    while (true) {
+        size_t count =
+                mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
+        if (count == 0) {
+            break;
+        }
+        events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+    }
+    if (DEBUG) {
+        dumpEvents(events);
+    }
+    return events;
+}
+
+/**
+ * Since the test runs on a real platform, there will be existing devices
+ * in addition to the test devices being added. Therefore, when EventHub is first created,
+ * it will return a lot of "device added" type of events.
+ */
+void EventHubTest::consumeInitialDeviceAddedEvents() {
+    std::vector<RawEvent> events = getEvents(0ms);
+    std::set<int32_t /*deviceId*/> existingDevices;
+    // All of the events should be DEVICE_ADDED type, except the last one.
+    for (size_t i = 0; i < events.size() - 1; i++) {
+        const RawEvent& event = events[i];
+        EXPECT_EQ(EventHubInterface::DEVICE_ADDED, event.type);
+        existingDevices.insert(event.deviceId);
+    }
+    // None of the existing system devices should be changing while this test is run.
+    // Check that the returned device ids are unique for all of the existing devices.
+    EXPECT_EQ(existingDevices.size(), events.size() - 1);
+    // The last event should be "finished device scan"
+    EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
+}
+
+int32_t EventHubTest::waitForDeviceCreation() {
+    // Wait a little longer than usual, to ensure input device has time to be created
+    std::vector<RawEvent> events = getEvents(20ms);
+    EXPECT_EQ(2U, events.size()); // Using "expect" because the function is non-void.
+    const RawEvent& deviceAddedEvent = events[0];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
+    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
+    const int32_t deviceId = deviceAddedEvent.deviceId;
+    EXPECT_EQ(identifier.name, mKeyboard->getName());
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
+    return deviceId;
+}
+
+void EventHubTest::waitForDeviceClose(int32_t deviceId) {
+    std::vector<RawEvent> events = getEvents(20ms);
+    ASSERT_EQ(2U, events.size());
+    const RawEvent& deviceRemovedEvent = events[0];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
+    EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
+    const RawEvent& finishedDeviceScanEvent = events[1];
+    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+              finishedDeviceScanEvent.type);
+}
+
+/**
+ * Ensure that input_events are generated with monotonic clock.
+ * That means input_event should receive a timestamp that is in the future of the time
+ * before the event was sent.
+ * Input system uses CLOCK_MONOTONIC everywhere in the code base.
+ */
+TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
+    nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    ASSERT_NO_FATAL_FAILURE(mKeyboard->pressAndReleaseHomeKey());
+
+    std::vector<RawEvent> events = getEvents();
+    ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
+    for (const RawEvent& event : events) {
+        // Cannot use strict comparison because the events may happen too quickly
+        ASSERT_LE(lastEventTime, event.when) << "Event must have occurred after the key was sent";
+        ASSERT_LT(std::chrono::nanoseconds(event.when - lastEventTime), 100ms)
+                << "Event times are too far apart";
+        lastEventTime = event.when; // Ensure all returned events are monotonic
+    }
+}
diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp
index 813b69e..f58b628 100644
--- a/services/inputflinger/tests/InputClassifierConverter_test.cpp
+++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp
@@ -38,12 +38,15 @@
     coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2);
     coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5);
     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*/,
-            1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/,
-            downTime, {}/*videoFrames*/);
+    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, 1 /*pointerCount*/, &properties,
+                                &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
+                                {} /*videoFrames*/);
     return motionArgs;
 }
 
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 7cc17a2..40086ef 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -38,12 +38,15 @@
     coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
     coords.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*/,
-            1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/,
-            downTime, {}/*videoFrames*/);
+    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, 1 /*pointerCount*/, &properties,
+                                &coords, 0 /*xPrecision*/, 0 /*yPrecision*/,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime,
+                                {} /*videoFrames*/);
     return motionArgs;
 }
 
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 551bee1..c4092cd 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,12 +16,12 @@
 
 #include "../dispatcher/InputDispatcher.h"
 
-#include <InputDispatcherThread.h>
-
 #include <binder/Binder.h>
+#include <input/Input.h>
 
 #include <gtest/gtest.h>
 #include <linux/input.h>
+#include <vector>
 
 namespace android::inputdispatcher {
 
@@ -38,6 +38,22 @@
 static const int32_t INJECTOR_PID = 999;
 static const int32_t INJECTOR_UID = 1001;
 
+struct PointF {
+    float x;
+    float y;
+};
+
+/**
+ * Return a DOWN key event with KEYCODE_A.
+ */
+static KeyEvent getTestKeyEvent() {
+    KeyEvent event;
+
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+                     ARBITRARY_TIME);
+    return event;
+}
 
 // --- FakeInputDispatcherPolicy ---
 
@@ -50,60 +66,55 @@
 
 public:
     FakeInputDispatcherPolicy() {
-        mInputEventFiltered = false;
-        mTime = -1;
-        mAction = -1;
-        mDisplayId = -1;
-        mOnPointerDownToken.clear();
     }
 
-    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 assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
+        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
+                                        args.displayId);
     }
 
-    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 assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
+        assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
+                                        args.displayId);
     }
 
-    void assertFilterInputEventWasNotCalled() {
-        ASSERT_FALSE(mInputEventFiltered)
-                << "Expected filterInputEvent() to not have been called.";
+    void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+
+    void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+        ASSERT_TRUE(mConfigurationChangedTime)
+                << "Timed out waiting for configuration changed call";
+        ASSERT_EQ(*mConfigurationChangedTime, when);
+        mConfigurationChangedTime = std::nullopt;
+    }
+
+    void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+        ASSERT_TRUE(mLastNotifySwitch);
+        // We do not check sequenceNum because it is not exposed to the policy
+        EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
+        EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+        EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+        EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+        mLastNotifySwitch = std::nullopt;
     }
 
     void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
-        ASSERT_EQ(mOnPointerDownToken, touchedToken)
-                << "Expected token from onPointerDownOutsideFocus was not matched";
-        reset();
+        ASSERT_EQ(touchedToken, mOnPointerDownToken);
+        mOnPointerDownToken.clear();
+    }
+
+    void assertOnPointerDownWasNotCalled() {
+        ASSERT_TRUE(mOnPointerDownToken == nullptr)
+                << "Expected onPointerDownOutsideFocus to not have been called";
     }
 
 private:
-    bool mInputEventFiltered;
-    nsecs_t mTime;
-    int32_t mAction;
-    int32_t mDisplayId;
+    std::unique_ptr<InputEvent> mFilteredEvent;
+    std::optional<nsecs_t> mConfigurationChangedTime;
     sp<IBinder> mOnPointerDownToken;
+    std::optional<NotifySwitchArgs> mLastNotifySwitch;
 
-    virtual void notifyConfigurationChanged(nsecs_t) {
+    virtual void notifyConfigurationChanged(nsecs_t when) override {
+        mConfigurationChangedTime = when;
     }
 
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
@@ -122,26 +133,20 @@
         *outConfig = mConfig;
     }
 
-    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
+    virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
         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();
+                mFilteredEvent = std::make_unique<KeyEvent>(*keyEvent);
                 break;
             }
 
             case AINPUT_EVENT_TYPE_MOTION: {
                 const MotionEvent* motionEvent = static_cast<const MotionEvent*>(inputEvent);
-                mTime = motionEvent->getEventTime();
-                mAction = motionEvent->getAction();
-                mDisplayId = motionEvent->getDisplayId();
+                mFilteredEvent = std::make_unique<MotionEvent>(*motionEvent);
                 break;
             }
         }
-
-        mInputEventFiltered = true;
         return true;
     }
 
@@ -161,7 +166,13 @@
         return false;
     }
 
-    virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) {
+    virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+                              uint32_t policyFlags) override {
+        /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
+         * essentially a passthrough for notifySwitch.
+         */
+        mLastNotifySwitch =
+                NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask);
     }
 
     virtual void pokeUserActivity(nsecs_t, int32_t) {
@@ -175,15 +186,92 @@
         mOnPointerDownToken = newToken;
     }
 
-    void reset() {
-        mInputEventFiltered = false;
-        mTime = -1;
-        mAction = -1;
-        mDisplayId = -1;
-        mOnPointerDownToken.clear();
+    void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
+                                         int32_t displayId) {
+        ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+        ASSERT_EQ(mFilteredEvent->getType(), type);
+
+        if (type == AINPUT_EVENT_TYPE_KEY) {
+            const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
+            EXPECT_EQ(keyEvent.getEventTime(), eventTime);
+            EXPECT_EQ(keyEvent.getAction(), action);
+            EXPECT_EQ(keyEvent.getDisplayId(), displayId);
+        } else if (type == AINPUT_EVENT_TYPE_MOTION) {
+            const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
+            EXPECT_EQ(motionEvent.getEventTime(), eventTime);
+            EXPECT_EQ(motionEvent.getAction(), action);
+            EXPECT_EQ(motionEvent.getDisplayId(), displayId);
+        } else {
+            FAIL() << "Unknown type: " << type;
+        }
+
+        mFilteredEvent = nullptr;
     }
 };
 
+// --- HmacKeyManagerTest ---
+
+class HmacKeyManagerTest : public testing::Test {
+protected:
+    HmacKeyManager mHmacKeyManager;
+};
+
+/**
+ * Ensure that separate calls to sign the same data are generating the same key.
+ * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
+ * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky
+ * tests.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+
+    std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent);
+    std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent);
+    ASSERT_EQ(hmac1, hmac2);
+}
+
+/**
+ * Ensure that changes in VerifiedKeyEvent produce a different hmac.
+ */
+TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) {
+    KeyEvent event = getTestKeyEvent();
+    VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event);
+    std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent);
+
+    verifiedEvent.deviceId += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.source += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.eventTimeNanos += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.displayId += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.action += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.downTimeNanos += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.flags += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.keyCode += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.scanCode += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.metaState += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+
+    verifiedEvent.repeatCount += 1;
+    ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent));
+}
 
 // --- InputDispatcherTest ---
 
@@ -191,20 +279,17 @@
 protected:
     sp<FakeInputDispatcherPolicy> mFakePolicy;
     sp<InputDispatcher> mDispatcher;
-    sp<InputDispatcherThread> mDispatcherThread;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         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);
+        ASSERT_EQ(OK, mDispatcher->start());
     }
 
-    virtual void TearDown() {
-        mDispatcherThread->requestExit();
-        mDispatcherThread.clear();
+    virtual void TearDown() override {
+        ASSERT_EQ(OK, mDispatcher->stop());
         mFakePolicy.clear();
         mDispatcher.clear();
     }
@@ -215,18 +300,18 @@
     KeyEvent event;
 
     // Rejects undefined key actions.
-    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
-            /*action*/ -1, 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
+                     ARBITRARY_TIME);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &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, ADISPLAY_ID_NONE,
-            AKEY_EVENT_ACTION_MULTIPLE, 0,
-            AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC,
+                     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,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
@@ -250,65 +335,82 @@
     constexpr MotionClassification classification = MotionClassification::NONE;
 
     // Rejects undefined motion actions.
-    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);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */,
+                     1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &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, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_DOWN |
+                             (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &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, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_DOWN |
+                             (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &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, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_UP |
+                             (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &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, source, DISPLAY_ID,
-            AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
-            0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0,
-            ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC,
+                     AMOTION_EVENT_ACTION_POINTER_UP |
+                             (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+                     0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */,
+                     0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &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, 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);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 0, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with 0 pointers.";
 
-    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);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
@@ -316,18 +418,22 @@
 
     // Rejects motion events with invalid pointer ids.
     pointerProperties[0].id = -1;
-    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);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &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, 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);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
@@ -336,18 +442,41 @@
     // Rejects motion events with duplicate pointer ids.
     pointerProperties[0].id = 1;
     pointerProperties[1].id = 1;
-    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);
+    event.initialize(DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0,
+                     edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, 0, 0,
+                     0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME,
+                     /*pointerCount*/ 2, pointerProperties, pointerCoords);
     ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(
             &event,
             INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0))
             << "Should reject motion events with duplicate pointer ids.";
 }
 
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
+TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
+    constexpr nsecs_t eventTime = 20;
+    NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime);
+    mDispatcher->notifyConfigurationChanged(&args);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+
+    mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
+}
+
+TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
+    NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/,
+                          1 /*switchValues*/, 2 /*switchMask*/);
+    mDispatcher->notifySwitch(&args);
+
+    // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
+    args.policyFlags |= POLICY_FLAG_TRUSTED;
+    mFakePolicy->assertNotifySwitchWasCalled(args);
+}
+
 // --- InputDispatcherTest SetInputWindowTest ---
-static const int32_t INJECT_EVENT_TIMEOUT = 500;
-static const int32_t DISPATCHING_TIMEOUT = 100;
+static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
+static constexpr int32_t DISPATCHING_TIMEOUT = 100;
 
 class FakeApplicationHandle : public InputApplicationHandle {
 public:
@@ -362,148 +491,255 @@
 
 class FakeInputReceiver {
 public:
-    void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId,
-            int32_t expectedFlags = 0) {
+    explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+          : mName(name) {
+        mConsumer = std::make_unique<InputConsumer>(clientChannel);
+    }
+
+    InputEvent* consume() {
         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.";
+        std::chrono::time_point start = std::chrono::steady_clock::now();
+        status_t status = WOULD_BLOCK;
+        while (status == WOULD_BLOCK) {
+            status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
+                                        &event);
+            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+            if (elapsed > 100ms) {
+                break;
+            }
+        }
+
+        if (status == WOULD_BLOCK) {
+            // Just means there's no event available.
+            return nullptr;
+        }
+
+        if (status != OK) {
+            ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
+            return nullptr;
+        }
+        if (event == nullptr) {
+            ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
+            return nullptr;
+        }
+
+        status = mConsumer->sendFinishedSignal(consumeSeq, true);
+        if (status != OK) {
+            ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+        }
+        return event;
+    }
+
+    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+                      int32_t expectedFlags) {
+        InputEvent* event = consume();
+
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
         ASSERT_EQ(expectedEventType, event->getType())
-                << mName.c_str() << ": event type should match.";
+                << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+                << " event, got " << inputEventTypeToString(event->getType()) << " event";
 
-        ASSERT_EQ(expectedDisplayId, event->getDisplayId())
-                << mName.c_str() << ": event displayId should be the same as expected.";
+        EXPECT_EQ(expectedDisplayId, event->getDisplayId());
 
-        int32_t flags;
         switch (expectedEventType) {
             case AINPUT_EVENT_TYPE_KEY: {
-                KeyEvent* typedEvent = static_cast<KeyEvent*>(event);
-                flags = typedEvent->getFlags();
+                const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
+                EXPECT_EQ(expectedAction, keyEvent.getAction());
+                EXPECT_EQ(expectedFlags, keyEvent.getFlags());
                 break;
             }
             case AINPUT_EVENT_TYPE_MOTION: {
-                MotionEvent* typedEvent = static_cast<MotionEvent*>(event);
-                flags = typedEvent->getFlags();
+                const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+                EXPECT_EQ(expectedAction, motionEvent.getAction());
+                EXPECT_EQ(expectedFlags, motionEvent.getFlags());
                 break;
             }
+            case AINPUT_EVENT_TYPE_FOCUS: {
+                FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
+            }
             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 consumeFocusEvent(bool hasFocus, bool inTouchMode) {
+        InputEvent* event = consume();
+        ASSERT_NE(nullptr, event) << mName.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+        ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+                << "Got " << inputEventTypeToString(event->getType())
+                << " event instead of FOCUS event";
+
+        ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+                << mName.c_str() << ": event displayId should always be NONE.";
+
+        FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+        EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+        EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
     }
 
     void assertNoEvents() {
-        uint32_t consumeSeq;
-        InputEvent* event;
-        status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
-            &consumeSeq, &event);
-        ASSERT_NE(OK, status)
+        InputEvent* event = consume();
+        ASSERT_EQ(nullptr, event)
                 << mName.c_str()
-                << ": should not have received any events, so consume(..) should not return OK.";
+                << ": should not have received any events, so consume() should return NULL";
     }
 
+    sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+
 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);
-        }
+    std::unique_ptr<InputConsumer> mConsumer;
+    PreallocatedInputEventFactory mEventFactory;
 
-        virtual ~FakeInputReceiver() {
-        }
-
-        // return true if the event has been handled.
-        virtual bool handled() {
-            return false;
-        }
-
-        sp<InputDispatcher> mDispatcher;
-        sp<InputChannel> mServerChannel, mClientChannel;
-        InputConsumer *mConsumer;
-        PreallocatedInputEventFactory mEventFactory;
-
-        std::string mName;
-        int32_t mDisplayId;
+    std::string mName;
 };
 
-class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+class FakeWindowHandle : public InputWindowHandle {
 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), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
-            mServerChannel->setToken(new BBinder());
-            mDispatcher->registerInputChannel(mServerChannel, displayId);
+                     const sp<InputDispatcher>& dispatcher, const std::string name,
+                     int32_t displayId, sp<IBinder> token = nullptr)
+          : mName(name) {
+        if (token == nullptr) {
+            sp<InputChannel> serverChannel, clientChannel;
+            InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+            mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+            dispatcher->registerInputChannel(serverChannel);
+            token = serverChannel->getConnectionToken();
+        }
 
-            inputApplicationHandle->updateInfo();
-            mInfo.applicationInfo = *inputApplicationHandle->getInfo();
-    }
+        inputApplicationHandle->updateInfo();
+        mInfo.applicationInfo = *inputApplicationHandle->getInfo();
 
-    virtual bool updateInfo() {
-        mInfo.token = mServerChannel ? mServerChannel->getToken() : nullptr;
-        mInfo.name = mName;
-        mInfo.layoutParamsFlags = mLayoutParamFlags;
+        mInfo.token = token;
+        mInfo.id = 0;
+        mInfo.name = name;
+        mInfo.layoutParamsFlags = 0;
         mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
         mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
-        mInfo.frameLeft = mFrame.left;
-        mInfo.frameTop = mFrame.top;
-        mInfo.frameRight = mFrame.right;
-        mInfo.frameBottom = mFrame.bottom;
+        mInfo.frameLeft = 0;
+        mInfo.frameTop = 0;
+        mInfo.frameRight = WIDTH;
+        mInfo.frameBottom = HEIGHT;
         mInfo.globalScaleFactor = 1.0;
-        mInfo.addTouchableRegion(mFrame);
+        mInfo.touchableRegion.clear();
+        mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
         mInfo.visible = true;
         mInfo.canReceiveKeys = true;
-        mInfo.hasFocus = mFocused;
+        mInfo.hasFocus = false;
         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;
+        mInfo.displayId = displayId;
     }
 
-    void setFocus() {
-        mFocused = true;
-    }
+    virtual bool updateInfo() { return true; }
+
+    void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; }
 
     void setFrame(const Rect& frame) {
-        mFrame.set(frame);
+        mInfo.frameLeft = frame.left;
+        mInfo.frameTop = frame.top;
+        mInfo.frameRight = frame.right;
+        mInfo.frameBottom = frame.bottom;
+        mInfo.touchableRegion.clear();
+        mInfo.addTouchableRegion(frame);
     }
 
-    void setLayoutParamFlags(int32_t flags) {
-        mLayoutParamFlags = flags;
+    void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+
+    void setId(int32_t id) { mInfo.id = id; }
+
+    void setWindowScale(float xScale, float yScale) {
+        mInfo.windowXScale = xScale;
+        mInfo.windowYScale = yScale;
     }
 
-    void releaseChannel() {
-        mServerChannel.clear();
-        InputWindowHandle::releaseChannel();
-    }
-protected:
-    virtual bool handled() {
-        return true;
+    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
+                     expectedFlags);
     }
 
-    bool mFocused;
-    Rect mFrame;
-    int32_t mLayoutParamFlags;
+    void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeMotionPointerDown(int32_t pointerIdx,
+            int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) {
+        int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN
+                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        int32_t action = AMOTION_EVENT_ACTION_POINTER_UP
+                | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
+            int32_t expectedFlags = 0) {
+        consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
+                     expectedFlags);
+    }
+
+    void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Cannot consume events from a window with no receiver";
+        mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode);
+    }
+
+    void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+                      int32_t expectedFlags) {
+        ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
+        mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
+                                     expectedFlags);
+    }
+
+    InputEvent* consume() {
+        if (mInputReceiver == nullptr) {
+            return nullptr;
+        }
+        return mInputReceiver->consume();
+    }
+
+    void assertNoEvents() {
+        ASSERT_NE(mInputReceiver, nullptr)
+                << "Call 'assertNoEvents' on a window with an InputReceiver";
+        mInputReceiver->assertNoEvents();
+    }
+
+    sp<IBinder> getToken() { return mInfo.token; }
+
+    const std::string& getName() { return mName; }
+
+private:
+    const std::string mName;
+    std::unique_ptr<FakeInputReceiver> mInputReceiver;
 };
 
 static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -512,9 +748,9 @@
     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);
+    event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, INVALID_HMAC,
+                     AKEY_EVENT_ACTION_DOWN, /* flags */ 0, AKEYCODE_A, KEY_A, AMETA_NONE,
+                     /* repeatCount */ 0, currentTime, currentTime);
 
     // Inject event until dispatch out.
     return dispatcher->injectInputEvent(
@@ -523,8 +759,10 @@
             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, int32_t x = 100, int32_t y = 200) {
+static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t action,
+                                 int32_t source, int32_t displayId, int32_t x, int32_t y,
+                                 int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                                 int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) {
     MotionEvent event;
     PointerProperties pointerProperties[1];
     PointerCoords pointerCoords[1];
@@ -539,12 +777,13 @@
 
     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);
+    event.initialize(DEVICE_ID, source, displayId, INVALID_HMAC, action, /* actionButton */ 0,
+                     /* flags */ 0,
+                     /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                     /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0,
+                     /* xPrecision */ 0, /* yPrecision */ 0, xCursorPosition, yCursorPosition,
+                     currentTime, currentTime,
+                     /*pointerCount*/ 1, pointerProperties, pointerCoords);
 
     // Inject event until dispatch out.
     return dispatcher->injectInputEvent(
@@ -553,6 +792,16 @@
             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, int32_t x = 100, int32_t y = 200) {
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y);
+}
+
+static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
+                              int32_t displayId, int32_t x = 100, int32_t y = 200) {
+    return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, x, y);
+}
+
 static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
     nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
     // Define a valid key event.
@@ -563,45 +812,55 @@
     return args;
 }
 
-static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
-    PointerProperties pointerProperties[1];
-    PointerCoords pointerCoords[1];
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId,
+                                           const std::vector<PointF>& points) {
+    size_t pointerCount = points.size();
+    if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) {
+        EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
+    }
 
-    pointerProperties[0].clear();
-    pointerProperties[0].id = 0;
-    pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+    PointerProperties pointerProperties[pointerCount];
+    PointerCoords pointerCoords[pointerCount];
 
-    pointerCoords[0].clear();
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
-    pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+    for (size_t i = 0; i < pointerCount; i++) {
+        pointerProperties[i].clear();
+        pointerProperties[i].id = i;
+        pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+        pointerCoords[i].clear();
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x);
+        pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y);
+    }
 
     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 */ {});
+                          POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
+                          AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+                          AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
+                          pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                          AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
 
     return args;
 }
 
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+    return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
+}
+
 TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
     sp<FakeApplicationHandle> application = new FakeApplicationHandle();
     sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
             ADISPLAY_ID_DEFAULT);
 
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(window);
-
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({window}, 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);
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
 }
 
 // The foreground window should receive the first touch down event.
@@ -612,17 +871,13 @@
     sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
             ADISPLAY_ID_DEFAULT);
 
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
-
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, 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);
+    windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowSecond->assertNoEvents();
 }
 
@@ -636,19 +891,17 @@
     // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    // Expect one focus window exist in display.
-    windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
+    // Display should have only one focused window
+    windowSecond->setFocus(true);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
 
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    windowSecond->consumeFocusEvent(true);
     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);
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) {
@@ -662,18 +915,16 @@
     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();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
+    windowTop->setFocus(true);
+    windowSecond->setFocus(true);
 
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+    windowTop->consumeFocusEvent(true);
     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);
+    windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
     windowSecond->assertNoEvents();
 }
 
@@ -688,14 +939,12 @@
     // Set focused application.
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
-    windowTop->setFocus();
-    windowSecond->setFocus();
-    std::vector<sp<InputWindowHandle>> inputWindowHandles;
-    inputWindowHandles.push_back(windowTop);
-    inputWindowHandles.push_back(windowSecond);
+    windowTop->setFocus(true);
+    windowSecond->setFocus(true);
     // Release channel for window is no longer valid.
     windowTop->releaseChannel();
-    mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
+    windowSecond->consumeFocusEvent(true);
 
     // Test inject a key down, should dispatch to a valid window.
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
@@ -703,41 +952,483 @@
 
     // Top window is invalid, so it should not receive any input event.
     windowTop->assertNoEvents();
-    windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE);
+    windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+}
+
+TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    sp<FakeWindowHandle> windowLeft =
+            new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+    windowLeft->setFrame(Rect(0, 0, 600, 800));
+    windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+    sp<FakeWindowHandle> windowRight =
+            new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+    windowRight->setFrame(Rect(600, 0, 1200, 800));
+    windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+    mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT);
+
+    // Inject an event with coordinate in the area of right window, with mouse cursor in the area of
+    // left window. This event should be dispatched to the left window.
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
+                                ADISPLAY_ID_DEFAULT, 610, 400, 599, 400));
+    windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    windowRight->assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    window->setFocus(true);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(true);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+
+    // Window should receive key down event.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+    // When device reset happens, that key stream should be terminated with FLAG_CANCELED
+    // on the app side.
+    NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+    mDispatcher->notifyDeviceReset(&args);
+    window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+                         AKEY_EVENT_FLAG_CANCELED);
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Window should receive motion down event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
+    // on the app side.
+    NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+    mDispatcher->notifyDeviceReset(&args);
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT,
+                         0 /*expectedFlags*/);
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    // Create a couple of windows
+    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+            "First Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+            "Second Window", ADISPLAY_ID_DEFAULT);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+
+    // Send down to the first window
+    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&downMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Transfer touch focus to the second window
+    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // The first window gets cancel and the second gets down
+    firstWindow->consumeMotionCancel();
+    secondWindow->consumeMotionDown();
+
+    // Send up event to the second window
+    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first  window gets no events and the second gets up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    PointF touchPoint = {10, 10};
+
+    // Create a couple of windows
+    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+            "First Window", ADISPLAY_ID_DEFAULT);
+    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+            "Second Window", ADISPLAY_ID_DEFAULT);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+
+    // Send down to the first window
+    NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint});
+    mDispatcher->notifyMotion(&downMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Send pointer down to the first window
+    NotifyMotionArgs pointerDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+    mDispatcher->notifyMotion(&pointerDownMotionArgs);
+    // Only the first window should get the pointer down event
+    firstWindow->consumeMotionPointerDown(1);
+    secondWindow->assertNoEvents();
+
+    // Transfer touch focus to the second window
+    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // The first window gets cancel and the second gets down and pointer down
+    firstWindow->consumeMotionCancel();
+    secondWindow->consumeMotionDown();
+    secondWindow->consumeMotionPointerDown(1);
+
+    // Send pointer up to the second window
+    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint});
+    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    // The first window gets nothing and the second gets pointer up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionPointerUp(1);
+
+    // Send up event to the second window
+    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first window gets nothing and the second gets up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher,
+            "First Window", ADISPLAY_ID_DEFAULT);
+    firstWindow->setFrame(Rect(0, 0, 600, 400));
+    firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
+            | InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+    // Create a non touch modal window that supports split touch
+    sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher,
+            "Second Window", ADISPLAY_ID_DEFAULT);
+    secondWindow->setFrame(Rect(0, 400, 600, 800));
+    secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL
+            | InputWindowInfo::FLAG_SPLIT_TOUCH);
+
+    // Add the windows to the dispatcher
+    mDispatcher->setInputWindows({firstWindow, secondWindow}, ADISPLAY_ID_DEFAULT);
+
+    PointF pointInFirst = {300, 200};
+    PointF pointInSecond = {300, 600};
+
+    // Send down to the first window
+    NotifyMotionArgs firstDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst});
+    mDispatcher->notifyMotion(&firstDownMotionArgs);
+    // Only the first window should get the down event
+    firstWindow->consumeMotionDown();
+    secondWindow->assertNoEvents();
+
+    // Send down to the second window
+    NotifyMotionArgs secondDownMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_DOWN
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&secondDownMotionArgs);
+    // The first window gets a move and the second a down
+    firstWindow->consumeMotionMove();
+    secondWindow->consumeMotionDown();
+
+    // Transfer touch focus to the second window
+    mDispatcher->transferTouchFocus(firstWindow->getToken(), secondWindow->getToken());
+    // The first window gets cancel and the new gets pointer down (it already saw down)
+    firstWindow->consumeMotionCancel();
+    secondWindow->consumeMotionPointerDown(1);
+
+    // Send pointer up to the second window
+    NotifyMotionArgs pointerUpMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_POINTER_UP
+            | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {pointInFirst, pointInSecond});
+    mDispatcher->notifyMotion(&pointerUpMotionArgs);
+    // The first window gets nothing and the second gets pointer up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionPointerUp(1);
+
+    // Send up event to the second window
+    NotifyMotionArgs upMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP,
+            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&upMotionArgs);
+    // The first window gets nothing and the second gets up
+    firstWindow->assertNoEvents();
+    secondWindow->consumeMotionUp();
+}
+
+TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    window->setFocus(true);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    window->consumeFocusEvent(true);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+
+    // Window should receive key down event.
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    mDispatcher->waitForIdle();
+
+    window->assertNoEvents();
+}
+
+// If a window is touchable, but does not have focus, it should receive motion events, but not keys
+TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    // Send key
+    NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyKey(&keyArgs);
+    // Send motion
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Window should receive only the motion event
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    window->assertNoEvents(); // Key event or focus event will not be received
+}
+
+class FakeMonitorReceiver {
+public:
+    FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+                        int32_t displayId, bool isGestureMonitor = false) {
+        sp<InputChannel> serverChannel, clientChannel;
+        InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+        mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+        dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+    }
+
+    sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+
+    void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+        mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
+                                     expectedDisplayId, expectedFlags);
+    }
+
+    void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+
+private:
+    std::unique_ptr<FakeInputReceiver> mInputReceiver;
+};
+
+// Tests for gesture monitors
+TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocus(true);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(true);
+
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+            << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+    monitor.assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+                                                      true /*isGestureMonitor*/);
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    window->releaseChannel();
+
+    mDispatcher->pilferPointers(monitor.getToken());
+
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+            << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+    monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, TestMoveEvent) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT);
+
+    mDispatcher->notifyMotion(&motionArgs);
+    // Window should receive motion down event.
+    window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+    motionArgs.action = AMOTION_EVENT_ACTION_MOVE;
+    motionArgs.sequenceNum += 1;
+    motionArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+                                             motionArgs.pointerCoords[0].getX() - 10);
+
+    mDispatcher->notifyMotion(&motionArgs);
+    window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
+                         0 /*expectedFlags*/);
+}
+
+/**
+ * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to
+ * the device default right away. In the test scenario, we check both the default value,
+ * and the action of enabling / disabling.
+ */
+TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) {
+    sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+    sp<FakeWindowHandle> window =
+            new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+    // Set focused application.
+    mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+    window->setFocus(true);
+
+    SCOPED_TRACE("Check default value of touch mode");
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    SCOPED_TRACE("Remove the window to trigger focus loss");
+    window->setFocus(false);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/);
+
+    SCOPED_TRACE("Disable touch mode");
+    mDispatcher->setInTouchMode(false);
+    window->setFocus(true);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/);
+
+    SCOPED_TRACE("Remove the window to trigger focus loss");
+    window->setFocus(false);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/);
+
+    SCOPED_TRACE("Enable touch mode again");
+    mDispatcher->setInTouchMode(true);
+    window->setFocus(true);
+    mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+    window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/);
+
+    window->assertNoEvents();
 }
 
 /* Test InputDispatcher for MultiDisplay */
 class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
 public:
     static constexpr int32_t SECOND_DISPLAY_ID = 1;
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
         application1 = new FakeApplicationHandle();
         windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
                 ADISPLAY_ID_DEFAULT);
-        std::vector<sp<InputWindowHandle>> inputWindowHandles;
-        inputWindowHandles.push_back(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);
+        windowInPrimary->setFocus(true);
+        mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT);
+        windowInPrimary->consumeFocusEvent(true);
 
         application2 = new FakeApplicationHandle();
         windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
                 SECOND_DISPLAY_ID);
         // Set focus to second display window.
-        std::vector<sp<InputWindowHandle>> inputWindowHandles_Second;
-        inputWindowHandles_Second.push_back(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);
+        windowInSecondary->setFocus(true);
+        mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID);
+        windowInSecondary->consumeFocusEvent(true);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         InputDispatcherTest::TearDown();
 
         application1.clear();
@@ -758,7 +1449,7 @@
     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);
+    windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
 
     // Test touch down on second display.
@@ -766,71 +1457,61 @@
             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);
+    windowInSecondary->consumeMotionDown(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);
+    windowInPrimary->consumeKeyDown(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);
+    windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
 
-    // Remove secondary display.
-    std::vector<sp<InputWindowHandle>> noWindows;
-    mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
+    // Remove all windows in secondary display.
+    mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID);
 
     // Expect old focus should receive a cancel event.
-    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE,
-            AKEY_EVENT_FLAG_CANCELED);
+    windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, 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->consumeFocusEvent(false);
     windowInSecondary->assertNoEvents();
 }
 
-class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
-public:
-    FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
-            int32_t displayId, bool isGestureMonitor = false)
-            : FakeInputReceiver(dispatcher, name, displayId) {
-        mServerChannel->setToken(new BBinder());
-        mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor);
-    }
-};
-
 // 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);
+    FakeMonitorReceiver monitorInPrimary =
+            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitorInSecondary =
+            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);
+    windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+    monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
     windowInSecondary->assertNoEvents();
-    monitorInSecondary->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);
+    monitorInPrimary.assertNoEvents();
+    windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+    monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
 
     // Test inject a non-pointer motion event.
     // If specific a display, it will dispatch to the focused window of particular display,
@@ -839,26 +1520,26 @@
         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);
+    monitorInPrimary.assertNoEvents();
+    windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
+    monitorInSecondary.consumeMotionDown(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);
+    FakeMonitorReceiver monitorInPrimary =
+            FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+    FakeMonitorReceiver monitorInSecondary =
+            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);
+    monitorInPrimary.assertNoEvents();
+    windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+    monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
 class InputFilterTest : public InputDispatcherTest {
@@ -874,9 +1555,9 @@
         motionArgs = generateMotionArgs(
                 AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
         mDispatcher->notifyMotion(&motionArgs);
-
+        ASSERT_TRUE(mDispatcher->waitForIdle());
         if (expectToBeFiltered) {
-            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&motionArgs);
+            mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
         } else {
             mFakePolicy->assertFilterInputEventWasNotCalled();
         }
@@ -889,9 +1570,10 @@
         mDispatcher->notifyKey(&keyArgs);
         keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
         mDispatcher->notifyKey(&keyArgs);
+        ASSERT_TRUE(mDispatcher->waitForIdle());
 
         if (expectToBeFiltered) {
-            mFakePolicy->assertFilterInputEventWasCalledWithExpectedArgs(&keyArgs);
+            mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
         } else {
             mFakePolicy->assertFilterInputEventWasNotCalled();
         }
@@ -934,7 +1616,7 @@
 }
 
 class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputDispatcherTest::SetUp();
 
         sp<FakeApplicationHandle> application = new FakeApplicationHandle();
@@ -945,34 +1627,32 @@
         // window.
         mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
 
-        mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
-                ADISPLAY_ID_DEFAULT);
-        mWindowFocused->setFrame(Rect(50, 50, 100, 100));
-        mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
-        mWindowFocusedTouchPoint = 60;
+        mFocusedWindow =
+                new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+        mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+        mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+        mFocusedWindowTouchPoint = 60;
 
         // Set focused application.
         mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
-        mWindowFocused->setFocus();
+        mFocusedWindow->setFocus(true);
 
         // Expect one focus window exist in display.
-        std::vector<sp<InputWindowHandle>> inputWindowHandles;
-        inputWindowHandles.push_back(mUnfocusedWindow);
-        inputWindowHandles.push_back(mWindowFocused);
-        mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+        mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT);
+        mFocusedWindow->consumeFocusEvent(true);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         InputDispatcherTest::TearDown();
 
         mUnfocusedWindow.clear();
-        mWindowFocused.clear();
+        mFocusedWindow.clear();
     }
 
 protected:
     sp<FakeWindowHandle> mUnfocusedWindow;
-    sp<FakeWindowHandle> mWindowFocused;
-    int32_t mWindowFocusedTouchPoint;
+    sp<FakeWindowHandle> mFocusedWindow;
+    int32_t mFocusedWindowTouchPoint;
 };
 
 // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -982,9 +1662,8 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
+    ASSERT_TRUE(mDispatcher->waitForIdle());
     mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
 }
 
@@ -995,10 +1674,9 @@
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
             AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
 // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
@@ -1006,10 +1684,9 @@
 TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
     ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
             << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
 }
 
 // Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1017,14 +1694,244 @@
 // onPointerDownOutsideFocus callback.
 TEST_F(InputDispatcherOnPointerDownOutsideFocus,
         OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
-    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
-            AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
-            mWindowFocusedTouchPoint))
+    ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+              injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+                               mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
             << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
-    // Call monitor to wait for the command queue to get flushed.
-    mDispatcher->monitor();
 
-    mFakePolicy->assertOnPointerDownEquals(nullptr);
+    ASSERT_TRUE(mDispatcher->waitForIdle());
+    mFakePolicy->assertOnPointerDownWasNotCalled();
+}
+
+// These tests ensures we can send touch events to a single client when there are multiple input
+// windows that point to the same client token.
+class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
+    virtual void SetUp() override {
+        InputDispatcherTest::SetUp();
+
+        sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+        mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
+                                        ADISPLAY_ID_DEFAULT);
+        // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
+        // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
+        mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+                                      InputWindowInfo::FLAG_SPLIT_TOUCH);
+        mWindow1->setId(0);
+        mWindow1->setFrame(Rect(0, 0, 100, 100));
+
+        mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
+                                        ADISPLAY_ID_DEFAULT, mWindow1->getToken());
+        mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+                                      InputWindowInfo::FLAG_SPLIT_TOUCH);
+        mWindow2->setId(1);
+        mWindow2->setFrame(Rect(100, 100, 200, 200));
+
+        mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT);
+    }
+
+protected:
+    sp<FakeWindowHandle> mWindow1;
+    sp<FakeWindowHandle> mWindow2;
+
+    // Helper function to convert the point from screen coordinates into the window's space
+    static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
+        float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
+        float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
+        return {x, y};
+    }
+
+    void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
+                            const std::vector<PointF>& points) {
+        const std::string name = window->getName();
+        InputEvent* event = window->consume();
+
+        ASSERT_NE(nullptr, event) << name.c_str()
+                                  << ": consumer should have returned non-NULL event.";
+
+        ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+                << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
+                << " event, got " << inputEventTypeToString(event->getType()) << " event";
+
+        const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+        EXPECT_EQ(expectedAction, motionEvent.getAction());
+
+        for (size_t i = 0; i < points.size(); i++) {
+            float expectedX = points[i].x;
+            float expectedY = points[i].y;
+
+            EXPECT_EQ(expectedX, motionEvent.getX(i))
+                    << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
+                    << ", got " << motionEvent.getX(i);
+            EXPECT_EQ(expectedY, motionEvent.getY(i))
+                    << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
+                    << ", got " << motionEvent.getY(i);
+        }
+    }
+};
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
+    // Touch Window 1
+    PointF touchedPoint = {10, 10};
+    PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+    // Release touch on Window 1
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    // consume the UP event
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+    // Touch Window 2
+    touchedPoint = {150, 150};
+    expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+    mWindow2->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    PointF touchedPoint = {10, 10};
+    PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+    // Release touch on Window 1
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+    // consume the UP event
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+    // Touch Window 2
+    touchedPoint = {150, 150};
+    expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, {touchedPoint});
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+    mWindow2->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    std::vector<PointF> touchedPoints = {PointF{10, 10}};
+    std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchedPoints.emplace_back(PointF{150, 150});
+    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+    mWindow2->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    std::vector<PointF> touchedPoints = {PointF{10, 10}};
+    std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchedPoints.emplace_back(PointF{150, 150});
+    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+    // Move both windows
+    touchedPoints = {{20, 20}, {175, 175}};
+    expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+                      getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
+    mWindow1->setWindowScale(0.5f, 0.5f);
+
+    // Touch Window 1
+    std::vector<PointF> touchedPoints = {PointF{10, 10}};
+    std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+    NotifyMotionArgs motionArgs =
+            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+                               ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+    // Touch Window 2
+    int32_t actionPointerDown =
+            AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+    touchedPoints.emplace_back(PointF{150, 150});
+    expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+    motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    // Consuming from window1 since it's the window that has the InputReceiver
+    consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+    // Move both windows
+    touchedPoints = {{20, 20}, {175, 175}};
+    expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+                      getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+    motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+                                    ADISPLAY_ID_DEFAULT, touchedPoints);
+    mDispatcher->notifyMotion(&motionArgs);
+
+    consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
 }
 
 } // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index aeb4ad6..7cd8793 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -25,13 +25,18 @@
 #include <TestInputListener.h>
 #include <TouchInputMapper.h>
 
+#include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
 #include <inttypes.h>
 #include <math.h>
 
-
 namespace android {
 
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
+
 // An arbitrary time value.
 static const nsecs_t ARBITRARY_TIME = 1234;
 
@@ -85,10 +90,6 @@
         mMaxY = maxY;
     }
 
-    void setDisplayId(int32_t displayId) {
-        mDisplayId = displayId;
-    }
-
     virtual void setPosition(float x, float y) {
         mX = x;
         mY = y;
@@ -111,6 +112,10 @@
         return mDisplayId;
     }
 
+    virtual void setDisplayViewport(const DisplayViewport& viewport) {
+        mDisplayId = viewport.displayId;
+    }
+
     const std::map<int32_t, std::vector<int32_t>>& getSpots() {
         return mSpotsByDisplay;
     }
@@ -164,9 +169,13 @@
 // --- FakeInputReaderPolicy ---
 
 class FakeInputReaderPolicy : public InputReaderPolicyInterface {
+    std::mutex mLock;
+    std::condition_variable mDevicesChangedCondition;
+
     InputReaderConfiguration mConfig;
     KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
-    std::vector<InputDeviceInfo> mInputDevices;
+    std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
+    bool mInputDevicesChanged GUARDED_BY(mLock){false};
     std::vector<DisplayViewport> mViewports;
     TouchAffineTransformation transform;
 
@@ -177,6 +186,20 @@
     FakeInputReaderPolicy() {
     }
 
+    void assertInputDevicesChanged() {
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+
+        const bool devicesChanged =
+                mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mInputDevicesChanged;
+                });
+        if (!devicesChanged) {
+            FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+        }
+        mInputDevicesChanged = false;
+    }
+
     virtual void clearViewports() {
         mViewports.clear();
         mConfig.setDisplayViewports(mViewports);
@@ -202,6 +225,20 @@
         mConfig.setDisplayViewports(mViewports);
     }
 
+    bool updateViewport(const DisplayViewport& viewport) {
+        size_t count = mViewports.size();
+        for (size_t i = 0; i < count; i++) {
+            const DisplayViewport& currentViewport = mViewports[i];
+            if (currentViewport.displayId == viewport.displayId) {
+                mViewports[i] = viewport;
+                mConfig.setDisplayViewports(mViewports);
+                return true;
+            }
+        }
+        // no viewport found.
+        return false;
+    }
+
     void addExcludedDeviceName(const std::string& deviceName) {
         mConfig.excludedDeviceNames.push_back(deviceName);
     }
@@ -210,21 +247,9 @@
         mConfig.portAssociations.insert({inputPort, displayPort});
     }
 
-    void addDisabledDevice(int32_t deviceId) {
-        ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
-        bool currentlyEnabled = index < 0;
-        if (currentlyEnabled) {
-            mConfig.disabledDevices.add(deviceId);
-        }
-    }
+    void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); }
 
-    void removeDisabledDevice(int32_t deviceId) {
-        ssize_t index = mConfig.disabledDevices.indexOf(deviceId);
-        bool currentlyEnabled = index < 0;
-        if (!currentlyEnabled) {
-            mConfig.disabledDevices.remove(deviceId);
-        }
-    }
+    void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); }
 
     void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) {
         mPointerControllers.add(deviceId, controller);
@@ -255,6 +280,10 @@
         mConfig.showTouches = enabled;
     }
 
+    void setDefaultPointerDisplayId(int32_t pointerDisplayId) {
+        mConfig.defaultPointerDisplayId = pointerDisplayId;
+    }
+
 private:
     DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height,
             int32_t orientation, const std::string& uniqueId, std::optional<uint8_t> physicalPort,
@@ -289,7 +318,10 @@
     }
 
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mInputDevices = inputDevices;
+        mInputDevicesChanged = true;
+        mDevicesChangedCondition.notify_all();
     }
 
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
@@ -340,19 +372,21 @@
         }
     };
 
+    std::mutex mLock;
+    std::condition_variable mEventsCondition;
+
     KeyedVector<int32_t, Device*> mDevices;
     std::vector<std::string> mExcludedDevices;
-    List<RawEvent> mEvents;
+    List<RawEvent> mEvents GUARDED_BY(mLock);
     std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;
 
-protected:
+public:
     virtual ~FakeEventHub() {
         for (size_t i = 0; i < mDevices.size(); i++) {
             delete mDevices.valueAt(i);
         }
     }
 
-public:
     FakeEventHub() { }
 
     void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) {
@@ -495,6 +529,7 @@
 
     void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
             int32_t code, int32_t value) {
+        std::scoped_lock<std::mutex> lock(mLock);
         RawEvent event;
         event.when = when;
         event.deviceId = deviceId;
@@ -514,8 +549,14 @@
     }
 
     void assertQueueIsEmpty() {
-        ASSERT_EQ(size_t(0), mEvents.size())
-                << "Expected the event queue to be empty (fully consumed).";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool queueIsEmpty =
+                mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
+                                          [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
+        if (!queueIsEmpty) {
+            FAIL() << "Timed out waiting for EventHub queue to be emptied.";
+        }
     }
 
 private:
@@ -548,7 +589,7 @@
     virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
             RawAbsoluteAxisInfo* outAxisInfo) const {
         Device* device = getDevice(deviceId);
-        if (device) {
+        if (device && device->enabled) {
             ssize_t index = device->absoluteAxes.indexOfKey(axis);
             if (index >= 0) {
                 *outAxisInfo = device->absoluteAxes.valueAt(index);
@@ -618,12 +659,14 @@
     }
 
     virtual size_t getEvents(int, RawEvent* buffer, size_t) {
+        std::scoped_lock<std::mutex> lock(mLock);
         if (mEvents.empty()) {
             return 0;
         }
 
         *buffer = *mEvents.begin();
         mEvents.erase(mEvents.begin());
+        mEventsCondition.notify_all();
         return 1;
     }
 
@@ -780,7 +823,7 @@
 // --- FakeInputReaderContext ---
 
 class FakeInputReaderContext : public InputReaderContext {
-    sp<EventHubInterface> mEventHub;
+    std::shared_ptr<EventHubInterface> mEventHub;
     sp<InputReaderPolicyInterface> mPolicy;
     sp<InputListenerInterface> mListener;
     int32_t mGlobalMetaState;
@@ -789,12 +832,14 @@
     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), mNextSequenceNum(1) {
-    }
+    FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub,
+                           const sp<InputReaderPolicyInterface>& policy,
+                           const sp<InputListenerInterface>& listener)
+          : mEventHub(eventHub),
+            mPolicy(policy),
+            mListener(listener),
+            mGlobalMetaState(0),
+            mNextSequenceNum(1) {}
 
     virtual ~FakeInputReaderContext() { }
 
@@ -874,11 +919,13 @@
     KeyedVector<int32_t, int32_t> mScanCodeStates;
     KeyedVector<int32_t, int32_t> mSwitchStates;
     std::vector<int32_t> mSupportedKeyCodes;
-    RawEvent mLastEvent;
 
-    bool mConfigureWasCalled;
-    bool mResetWasCalled;
-    bool mProcessWasCalled;
+    std::mutex mLock;
+    std::condition_variable mStateChangedCondition;
+    bool mConfigureWasCalled GUARDED_BY(mLock);
+    bool mResetWasCalled GUARDED_BY(mLock);
+    bool mProcessWasCalled GUARDED_BY(mLock);
+    RawEvent mLastEvent GUARDED_BY(mLock);
 
     std::optional<DisplayViewport> mViewport;
 public:
@@ -900,20 +947,41 @@
     }
 
     void assertConfigureWasCalled() {
-        ASSERT_TRUE(mConfigureWasCalled)
-                << "Expected configure() to have been called.";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool configureCalled =
+                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mConfigureWasCalled;
+                });
+        if (!configureCalled) {
+            FAIL() << "Expected configure() to have been called.";
+        }
         mConfigureWasCalled = false;
     }
 
     void assertResetWasCalled() {
-        ASSERT_TRUE(mResetWasCalled)
-                << "Expected reset() to have been called.";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool resetCalled =
+                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mResetWasCalled;
+                });
+        if (!resetCalled) {
+            FAIL() << "Expected reset() to have been called.";
+        }
         mResetWasCalled = false;
     }
 
     void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
-        ASSERT_TRUE(mProcessWasCalled)
-                << "Expected process() to have been called.";
+        std::unique_lock<std::mutex> lock(mLock);
+        base::ScopedLockAssertion assumeLocked(mLock);
+        const bool processCalled =
+                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
+                    return mProcessWasCalled;
+                });
+        if (!processCalled) {
+            FAIL() << "Expected process() to have been called.";
+        }
         if (outLastEvent) {
             *outLastEvent = mLastEvent;
         }
@@ -950,6 +1018,7 @@
     }
 
     virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mConfigureWasCalled = true;
 
         // Find the associated viewport if exist.
@@ -957,15 +1026,21 @@
         if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
             mViewport = config->getDisplayViewportByPort(*displayPort);
         }
+
+        mStateChangedCondition.notify_all();
     }
 
     virtual void reset(nsecs_t) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mResetWasCalled = true;
+        mStateChangedCondition.notify_all();
     }
 
     virtual void process(const RawEvent* rawEvent) {
+        std::scoped_lock<std::mutex> lock(mLock);
         mLastEvent = *rawEvent;
         mProcessWasCalled = true;
+        mStateChangedCondition.notify_all();
     }
 
     virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
@@ -1019,12 +1094,10 @@
     InputDevice* mNextDevice;
 
 public:
-    InstrumentedInputReader(const sp<EventHubInterface>& eventHub,
-            const sp<InputReaderPolicyInterface>& policy,
-            const sp<InputListenerInterface>& listener) :
-            InputReader(eventHub, policy, listener),
-            mNextDevice(nullptr) {
-    }
+    InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub,
+                            const sp<InputReaderPolicyInterface>& policy,
+                            const sp<InputListenerInterface>& listener)
+          : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {}
 
     virtual ~InstrumentedInputReader() {
         if (mNextDevice) {
@@ -1032,23 +1105,25 @@
         }
     }
 
-    void setNextDevice(InputDevice* device) {
-        mNextDevice = device;
-    }
+    void setNextDevice(InputDevice* device) { mNextDevice = device; }
 
     InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
-            uint32_t classes, const std::string& location = "") {
+                           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);
+                               classes);
     }
 
+    // Make the protected loopOnce method accessible to tests.
+    using InputReader::loopOnce;
+
 protected:
     virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
-            const InputDeviceIdentifier& identifier, uint32_t classes) {
+                                            const InputDeviceIdentifier& identifier,
+                                            uint32_t classes) {
         if (mNextDevice) {
             InputDevice* device = mNextDevice;
             mNextDevice = nullptr;
@@ -1065,12 +1140,8 @@
 protected:
     sp<FakeInputReaderPolicy> mFakePolicy;
 
-    virtual void SetUp() {
-        mFakePolicy = new FakeInputReaderPolicy();
-    }
-    virtual void TearDown() {
-        mFakePolicy.clear();
-    }
+    virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+    virtual void TearDown() override { mFakePolicy.clear(); }
 };
 
 /**
@@ -1252,23 +1323,21 @@
 protected:
     sp<TestInputListener> mFakeListener;
     sp<FakeInputReaderPolicy> mFakePolicy;
-    sp<FakeEventHub> mFakeEventHub;
-    sp<InstrumentedInputReader> mReader;
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
+    std::unique_ptr<InstrumentedInputReader> mReader;
 
-    virtual void SetUp() {
-        mFakeEventHub = new FakeEventHub();
+    virtual void SetUp() override {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
 
-        mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
+        mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+                                                            mFakeListener);
     }
 
-    virtual void TearDown() {
-        mReader.clear();
-
+    virtual void TearDown() override {
         mFakeListener.clear();
         mFakePolicy.clear();
-        mFakeEventHub.clear();
     }
 
     void addDevice(int32_t deviceId, const std::string& name, uint32_t classes,
@@ -1281,29 +1350,26 @@
         mFakeEventHub->finishDeviceScan();
         mReader->loopOnce();
         mReader->loopOnce();
-        mFakeEventHub->assertQueueIsEmpty();
+        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+        ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
     }
 
-    void disableDevice(int32_t deviceId, InputDevice* device) {
+    void disableDevice(int32_t deviceId) {
         mFakePolicy->addDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
-    void enableDevice(int32_t deviceId, InputDevice* device) {
+    void enableDevice(int32_t deviceId) {
         mFakePolicy->removeDisabledDevice(deviceId);
-        configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+        mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
     }
 
-    void configureDevice(uint32_t changes, InputDevice* device) {
-        device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
-    }
-
-    FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
-            const std::string& name, uint32_t classes, uint32_t sources,
-            const PropertyMap* configuration) {
+    FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
+                                                  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);
-        device->addMapper(mapper);
+        FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(sources);
         mReader->setNextDevice(device);
         addDevice(deviceId, name, classes, configuration);
         return mapper;
@@ -1316,10 +1382,8 @@
     ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
             0, nullptr)); // no classes so device will be ignored
 
-
     std::vector<InputDeviceInfo> inputDevices;
     mReader->getInputDevices(inputDevices);
-
     ASSERT_EQ(1U, inputDevices.size());
     ASSERT_EQ(1, inputDevices[0].getId());
     ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
@@ -1342,46 +1406,42 @@
     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);
+    device->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
+    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
 
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
     ASSERT_EQ(deviceId, resetArgs.deviceId);
 
     ASSERT_EQ(device->isEnabled(), true);
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
 
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), false);
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasNotCalled();
-    mFakeListener->assertNotifyConfigurationChangedWasNotCalled();
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
     ASSERT_EQ(device->isEnabled(), false);
 
-    enableDevice(deviceId, device);
+    enableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
-    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_EQ(deviceId, resetArgs.deviceId);
     ASSERT_EQ(device->isEnabled(), true);
 }
 
 TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
-    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);
+    FakeInputMapper& 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,
             AINPUT_SOURCE_ANY, AKEYCODE_A))
@@ -1405,10 +1465,10 @@
 }
 
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
-    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);
+    FakeInputMapper& 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,
             AINPUT_SOURCE_ANY, KEY_A))
@@ -1432,10 +1492,10 @@
 }
 
 TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
-    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);
+    FakeInputMapper& 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,
             AINPUT_SOURCE_ANY, SW_LID))
@@ -1459,12 +1519,12 @@
 }
 
 TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
 
-    mapper->addSupportedKeyCode(AKEYCODE_A);
-    mapper->addSupportedKeyCode(AKEYCODE_B);
+    mapper.addSupportedKeyCode(AKEYCODE_A);
+    mapper.addSupportedKeyCode(AKEYCODE_B);
 
     const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 };
     uint8_t flags[4] = { 0, 0, 0, 1 };
@@ -1504,16 +1564,16 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
-    FakeInputMapper* mapper = nullptr;
-    ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, 0, "fake",
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, nullptr));
+    FakeInputMapper& mapper =
+            addDeviceWithFakeInputMapper(1, 0, "fake", INPUT_DEVICE_CLASS_KEYBOARD,
+                                         AINPUT_SOURCE_KEYBOARD, nullptr);
 
     mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, 1);
     mReader->loopOnce();
     ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
 
     RawEvent event;
-    ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event));
+    ASSERT_NO_FATAL_FAILURE(mapper.assertProcessWasCalled(&event));
     ASSERT_EQ(0, event.when);
     ASSERT_EQ(1, event.deviceId);
     ASSERT_EQ(EV_KEY, event.type);
@@ -1526,30 +1586,29 @@
     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);
+    device->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
+    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     uint32_t prevSequenceNum = resetArgs.sequenceNum;
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
-    enableDevice(deviceId, device);
+    enableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 
-    disableDevice(deviceId, device);
+    disableDevice(deviceId);
     mReader->loopOnce();
-    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
     ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
     prevSequenceNum = resetArgs.sequenceNum;
 }
@@ -1560,10 +1619,8 @@
     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);
+    FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
     mReader->setNextDevice(device);
-    addDevice(deviceId, "fake", deviceClass, nullptr);
 
     const uint8_t hdmi1 = 1;
 
@@ -1571,6 +1628,7 @@
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
 
     // Add default and second display.
+    mFakePolicy->clearViewports();
     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,
@@ -1578,32 +1636,45 @@
     mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
     mReader->loopOnce();
 
-    // Check device.
+    // Add the device, and make sure all of the callbacks are triggered.
+    // The device is added after the input port associations are processed since
+    // we do not yet support dynamic device-to-display associations.
+    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
+
+    // Device should only dispatch to the specified display.
     ASSERT_EQ(deviceId, device->getId());
     ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID));
     ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
+
+    // Can't dispatch event from a disabled device.
+    disableDevice(deviceId);
+    mReader->loopOnce();
+    ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
 }
 
 
 // --- InputDeviceTest ---
-
 class InputDeviceTest : 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;
     static const uint32_t DEVICE_CLASSES;
 
-    sp<FakeEventHub> mFakeEventHub;
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
 
     InputDevice* mDevice;
 
-    virtual void SetUp() {
-        mFakeEventHub = new FakeEventHub();
+    virtual void SetUp() override {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
@@ -1611,21 +1682,22 @@
         mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0);
         InputDeviceIdentifier identifier;
         identifier.name = DEVICE_NAME;
+        identifier.location = DEVICE_LOCATION;
         mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION,
                 DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         delete mDevice;
 
         delete mFakeContext;
         mFakeListener.clear();
         mFakePolicy.clear();
-        mFakeEventHub.clear();
     }
 };
 
 const char* InputDeviceTest::DEVICE_NAME = "device";
+const char* InputDeviceTest::DEVICE_LOCATION = "USB1";
 const int32_t InputDeviceTest::DEVICE_ID = 1;
 const int32_t InputDeviceTest::DEVICE_GENERATION = 2;
 const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0;
@@ -1688,21 +1760,19 @@
     // Configuration.
     mFakeEventHub->addConfigurationProperty(DEVICE_ID, String8("key"), String8("value"));
 
-    FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD);
-    mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    mapper1->setMetaState(AMETA_ALT_ON);
-    mapper1->addSupportedKeyCode(AKEYCODE_A);
-    mapper1->addSupportedKeyCode(AKEYCODE_B);
-    mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
-    mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
-    mapper1->setScanCodeState(2, AKEY_STATE_DOWN);
-    mapper1->setScanCodeState(3, AKEY_STATE_UP);
-    mapper1->setSwitchState(4, AKEY_STATE_DOWN);
-    mDevice->addMapper(mapper1);
+    FakeInputMapper& mapper1 = mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_KEYBOARD);
+    mapper1.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    mapper1.setMetaState(AMETA_ALT_ON);
+    mapper1.addSupportedKeyCode(AKEYCODE_A);
+    mapper1.addSupportedKeyCode(AKEYCODE_B);
+    mapper1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
+    mapper1.setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP);
+    mapper1.setScanCodeState(2, AKEY_STATE_DOWN);
+    mapper1.setScanCodeState(3, AKEY_STATE_UP);
+    mapper1.setSwitchState(4, AKEY_STATE_DOWN);
 
-    FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN);
-    mapper2->setMetaState(AMETA_SHIFT_ON);
-    mDevice->addMapper(mapper2);
+    FakeInputMapper& mapper2 = mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
+    mapper2.setMetaState(AMETA_SHIFT_ON);
 
     InputReaderConfiguration config;
     mDevice->configure(ARBITRARY_TIME, &config, 0);
@@ -1712,13 +1782,13 @@
             << "Device should have read configuration during configuration phase.";
     ASSERT_STREQ("value", propertyValue.string());
 
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertConfigureWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertConfigureWasCalled());
 
     // Reset
     mDevice->reset(ARBITRARY_TIME);
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertResetWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertResetWasCalled());
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -1774,10 +1844,52 @@
     RawEvent event;
     mDevice->process(&event, 1);
 
-    ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled());
-    ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper1.assertProcessWasCalled());
+    ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled());
 }
 
+// A single input device is associated with a specific display. Check that:
+// 1. Device is disabled if the viewport corresponding to the associated display is not found
+// 2. Device is disabled when setEnabled API is called
+TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) {
+    mDevice->addMapper<FakeInputMapper>(AINPUT_SOURCE_TOUCHSCREEN);
+
+    // First Configuration.
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0);
+
+    // Device should be enabled by default.
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Prepare associated info.
+    constexpr uint8_t hdmi = 1;
+    const std::string UNIQUE_ID = "local:1";
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    // Device should be disabled because it is associated with a specific display via
+    // input port <-> display port association, but the corresponding display is not found
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Prepare displays.
+    mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                                    DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi,
+                                    ViewportType::VIEWPORT_INTERNAL);
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_TRUE(mDevice->isEnabled());
+
+    // Device should be disabled after set disable.
+    mFakePolicy->addDisabledDevice(mDevice->getId());
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_ENABLED_STATE);
+    ASSERT_FALSE(mDevice->isEnabled());
+
+    // Device should still be disabled even found the associated display.
+    mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(mDevice->isEnabled());
+}
 
 // --- InputMapperTest ---
 
@@ -1790,14 +1902,14 @@
     static const int32_t DEVICE_CONTROLLER_NUMBER;
     static const uint32_t DEVICE_CLASSES;
 
-    sp<FakeEventHub> mFakeEventHub;
+    std::shared_ptr<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
     sp<TestInputListener> mFakeListener;
     FakeInputReaderContext* mFakeContext;
     InputDevice* mDevice;
 
-    virtual void SetUp() {
-        mFakeEventHub = new FakeEventHub();
+    virtual void SetUp() override {
+        mFakeEventHub = std::make_unique<FakeEventHub>();
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new TestInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
@@ -1810,12 +1922,11 @@
         mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
     }
 
-    virtual void TearDown() {
+    virtual void TearDown() override {
         delete mDevice;
         delete mFakeContext;
         mFakeListener.clear();
         mFakePolicy.clear();
-        mFakeEventHub.clear();
     }
 
     void addConfigurationProperty(const char* key, const char* value) {
@@ -1826,10 +1937,12 @@
         mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
     }
 
-    void addMapperAndConfigure(InputMapper* mapper) {
-        mDevice->addMapper(mapper);
+    template <class T, typename... Args>
+    T& addMapperAndConfigure(Args... args) {
+        T& mapper = mDevice->addMapper<T>(args...);
         configureDevice(0);
         mDevice->reset(ARBITRARY_TIME);
+        return mapper;
     }
 
     void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
@@ -1844,15 +1957,15 @@
         mFakePolicy->clearViewports();
     }
 
-    static void process(InputMapper* mapper, nsecs_t when, int32_t type,
-            int32_t code, int32_t value) {
+    static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code,
+                        int32_t value) {
         RawEvent event;
         event.when = when;
-        event.deviceId = mapper->getDeviceId();
+        event.deviceId = mapper.getDeviceId();
         event.type = type;
         event.code = code;
         event.value = value;
-        mapper->process(&event);
+        mapper.process(&event);
     }
 
     static void assertMotionRange(const InputDeviceInfo& info,
@@ -1906,26 +2019,23 @@
 };
 
 TEST_F(SwitchInputMapperTest, GetSources) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
-    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources());
+    ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper.getSources());
 }
 
 TEST_F(SwitchInputMapperTest, GetSwitchState) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
     mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1);
-    ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+    ASSERT_EQ(1, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 
     mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0);
-    ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
+    ASSERT_EQ(0, mapper.getSwitchState(AINPUT_SOURCE_ANY, SW_LID));
 }
 
 TEST_F(SwitchInputMapperTest, Process) {
-    SwitchInputMapper* mapper = new SwitchInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    SwitchInputMapper& mapper = addMapperAndConfigure<SwitchInputMapper>();
 
     process(mapper, ARBITRARY_TIME, EV_SW, SW_LID, 1);
     process(mapper, ARBITRARY_TIME, EV_SW, SW_JACK_PHYSICAL_INSERT, 1);
@@ -1950,8 +2060,9 @@
 
     void prepareDisplay(int32_t orientation);
 
-    void testDPadKeyRotation(KeyboardInputMapper* mapper,
-            int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode);
+    void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
+                             int32_t originalKeyCode, int32_t rotatedKeyCode,
+                             int32_t displayId = ADISPLAY_ID_NONE);
 };
 
 /* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
@@ -1962,8 +2073,9 @@
             orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL);
 }
 
-void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper,
-        int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) {
+void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
+                                                  int32_t originalScanCode, int32_t originalKeyCode,
+                                                  int32_t rotatedKeyCode, int32_t displayId) {
     NotifyKeyArgs args;
 
     process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1);
@@ -1971,21 +2083,22 @@
     ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
     ASSERT_EQ(originalScanCode, args.scanCode);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
+    ASSERT_EQ(displayId, args.displayId);
 
     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);
     ASSERT_EQ(rotatedKeyCode, args.keyCode);
+    ASSERT_EQ(displayId, args.displayId);
 }
 
-
 TEST_F(KeyboardInputMapperTest, GetSources) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
-    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources());
 }
 
 TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) {
@@ -1994,9 +2107,9 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
     mFakeEventHub->addKey(DEVICE_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Key down by scan code.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
@@ -2092,38 +2205,38 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Initial metastate.
-    ASSERT_EQ(AMETA_NONE, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 
     // Metakey down.
     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);
-    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 
     // Key down.
     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());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
 
     // Key up.
     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());
+    ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState());
 
     // Metakey up.
     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());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
     ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled());
 }
 
@@ -2133,9 +2246,9 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -2154,53 +2267,53 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     addConfigurationProperty("keyboard.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
 
     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,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
 
     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,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
 
     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,
-            KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT));
-    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
-            KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP));
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_UP, DISPLAY_ID));
 
     // 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.
@@ -2227,9 +2340,9 @@
     // 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);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     NotifyKeyArgs args;
 
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
@@ -2252,10 +2365,10 @@
     // 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);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
     NotifyKeyArgs args;
 
     // Display id should be ADISPLAY_ID_NONE without any display configuration.
@@ -2281,39 +2394,39 @@
 }
 
 TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1);
-    ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 
     mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0);
-    ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 }
 
 TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1);
-    ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 
     mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0);
-    ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 }
 
 TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     mFakeEventHub->addKey(DEVICE_ID, KEY_A, 0, AKEYCODE_A, 0);
 
     const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B };
     uint8_t flags[2] = { 0, 0 };
-    ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
+    ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags));
     ASSERT_TRUE(flags[0]);
     ASSERT_FALSE(flags[1]);
 }
@@ -2326,9 +2439,9 @@
     mFakeEventHub->addKey(DEVICE_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0);
     mFakeEventHub->addKey(DEVICE_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
 
-    KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice,
-            AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC);
-    addMapperAndConfigure(mapper);
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
 
     // Initialization should have turned all of the lights off.
     ASSERT_FALSE(mFakeEventHub->getLedState(DEVICE_ID, LED_CAPSL));
@@ -2341,7 +2454,7 @@
     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());
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
@@ -2349,7 +2462,7 @@
     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());
+    ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle caps lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1);
@@ -2357,7 +2470,7 @@
     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());
+    ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock on.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
@@ -2365,7 +2478,7 @@
     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());
+    ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle num lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1);
@@ -2373,7 +2486,7 @@
     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());
+    ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState());
 
     // Toggle scroll lock off.
     process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1);
@@ -2381,9 +2494,165 @@
     ASSERT_FALSE(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_NONE, mapper->getMetaState());
+    ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
 }
 
+TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
+    // keyboard 1.
+    mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+    // keyboard 2.
+    const std::string USB2 = "USB2";
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
+    InputDeviceIdentifier identifier;
+    identifier.name = "KEYBOARD2";
+    identifier.location = USB2;
+    std::unique_ptr<InputDevice> device2 =
+            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
+                                          DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+    mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    KeyboardInputMapper& mapper2 =
+            device2->addMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                    AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
+    device2->reset(ARBITRARY_TIME);
+
+    // Prepared displays and associated info.
+    constexpr uint8_t hdmi1 = 0;
+    constexpr uint8_t hdmi2 = 1;
+    const std::string SECONDARY_UNIQUE_ID = "local:1";
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
+    mFakePolicy->addInputPortAssociation(USB2, hdmi2);
+
+    // No associated display viewport found, should disable the device.
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+    ASSERT_FALSE(device2->isEnabled());
+
+    // Prepare second display.
+    constexpr int32_t newDisplayId = 2;
+    setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+                                 UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL);
+    setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0,
+                                 SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL);
+    // Default device will reconfigure above, need additional reconfiguration for another device.
+    device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+                       InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // Device should be enabled after the associated display is found.
+    ASSERT_TRUE(mDevice->isEnabled());
+    ASSERT_TRUE(device2->isEnabled());
+
+    // Test pad key events
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, DISPLAY_ID));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, DISPLAY_ID));
+
+    ASSERT_NO_FATAL_FAILURE(
+            testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT,
+                                                AKEYCODE_DPAD_RIGHT, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN,
+                                                AKEYCODE_DPAD_DOWN, newDisplayId));
+    ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT,
+                                                AKEYCODE_DPAD_LEFT, newDisplayId));
+}
+
+TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) {
+    // For external devices, non-media keys will trigger wake on key down. Media keys need to be
+    // marked as WAKE in the keylayout file to trigger wake.
+    mDevice->setExternal(true);
+
+    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) {
+    // Tv Remote key's wake behavior is prescribed by the keylayout file.
+    mDevice->setExternal(true);
+
+    mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+    mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+
+    addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
+    KeyboardInputMapper& mapper =
+            addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+                                                       AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+    NotifyKeyArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+    process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+    ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
 
 // --- CursorInputMapperTest ---
 
@@ -2393,15 +2662,15 @@
 
     sp<FakePointerController> mFakePointerController;
 
-    virtual void SetUp() {
+    virtual void SetUp() override {
         InputMapperTest::SetUp();
 
         mFakePointerController = new FakePointerController();
         mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController);
     }
 
-    void testMotionRotation(CursorInputMapper* mapper,
-            int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
+    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";
@@ -2413,8 +2682,9 @@
 
 const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
 
-void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper,
-        int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
+void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX,
+                                               int32_t originalY, int32_t rotatedX,
+                                               int32_t rotatedY) {
     NotifyMotionArgs args;
 
     process(mapper, ARBITRARY_TIME, EV_REL, REL_X, originalX);
@@ -2429,28 +2699,25 @@
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources());
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
 
     // Initially there may not be a valid motion range.
     ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
@@ -2462,7 +2729,7 @@
     mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
 
     InputDeviceInfo info2;
-    mapper->populateDeviceInfo(&info2);
+    mapper.populateDeviceInfo(&info2);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
@@ -2476,12 +2743,11 @@
 }
 
 TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
@@ -2495,9 +2761,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -2588,9 +2853,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2612,9 +2876,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2646,9 +2909,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -2694,9 +2956,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_90);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
@@ -2710,10 +2971,9 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "navigation");
     addConfigurationProperty("cursor.orientationAware", "1");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     prepareDisplay(DISPLAY_ORIENTATION_0);
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper,  0,  1,  0,  1));
@@ -2757,9 +3017,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -3045,9 +3304,8 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
     mFakePointerController->setPosition(100, 200);
@@ -3067,10 +3325,9 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_PointerCapture) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
     addConfigurationProperty("cursor.mode", "pointer");
     mFakePolicy->setPointerCapture(true);
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
     NotifyDeviceResetArgs resetArgs;
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -3156,15 +3413,20 @@
 }
 
 TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) {
-    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
-    addMapperAndConfigure(mapper);
+    CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>();
 
-    // Setup PointerController for second display.
+    // Setup for second display.
     constexpr int32_t SECOND_DISPLAY_ID = 1;
+    const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1";
+    mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0,
+                                    SECOND_DISPLAY_UNIQUE_ID, NO_PORT,
+                                    ViewportType::VIEWPORT_EXTERNAL);
+    mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID);
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
     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);
@@ -3179,7 +3441,6 @@
     ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId);
 }
 
-
 // --- TouchInputMapperTest ---
 
 class TouchInputMapperTest : public InputMapperTest {
@@ -3354,15 +3615,15 @@
     void prepareButtons();
     void prepareAxes(int axes);
 
-    void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processUp(SingleTouchInputMapper* mappery);
-    void processPressure(SingleTouchInputMapper* mapper, int32_t pressure);
-    void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor);
-    void processDistance(SingleTouchInputMapper* mapper, int32_t distance);
-    void processTilt(SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY);
-    void processKey(SingleTouchInputMapper* mapper, int32_t code, int32_t value);
-    void processSync(SingleTouchInputMapper* mapper);
+    void processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processUp(SingleTouchInputMapper& mappery);
+    void processPressure(SingleTouchInputMapper& mapper, int32_t pressure);
+    void processToolMajor(SingleTouchInputMapper& mapper, int32_t toolMajor);
+    void processDistance(SingleTouchInputMapper& mapper, int32_t distance);
+    void processTilt(SingleTouchInputMapper& mapper, int32_t tiltX, int32_t tiltY);
+    void processKey(SingleTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processSync(SingleTouchInputMapper& mapper);
 };
 
 void SingleTouchInputMapperTest::prepareButtons() {
@@ -3396,103 +3657,95 @@
     }
 }
 
-void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) {
+void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper& mapper, int32_t x, int32_t 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) {
+void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper& mapper, int32_t x, int32_t y) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, x);
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, y);
 }
 
-void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) {
+void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_KEY, BTN_TOUCH, 0);
 }
 
-void SingleTouchInputMapperTest::processPressure(
-        SingleTouchInputMapper* mapper, int32_t pressure) {
+void SingleTouchInputMapperTest::processPressure(SingleTouchInputMapper& mapper, int32_t pressure) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_PRESSURE, pressure);
 }
 
-void SingleTouchInputMapperTest::processToolMajor(
-        SingleTouchInputMapper* mapper, int32_t toolMajor) {
+void SingleTouchInputMapperTest::processToolMajor(SingleTouchInputMapper& mapper,
+                                                  int32_t toolMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_TOOL_WIDTH, toolMajor);
 }
 
-void SingleTouchInputMapperTest::processDistance(
-        SingleTouchInputMapper* mapper, int32_t distance) {
+void SingleTouchInputMapperTest::processDistance(SingleTouchInputMapper& mapper, int32_t distance) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_DISTANCE, distance);
 }
 
-void SingleTouchInputMapperTest::processTilt(
-        SingleTouchInputMapper* mapper, int32_t tiltX, int32_t tiltY) {
+void SingleTouchInputMapperTest::processTilt(SingleTouchInputMapper& mapper, int32_t tiltX,
+                                             int32_t 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) {
+void SingleTouchInputMapperTest::processKey(SingleTouchInputMapper& mapper, int32_t code,
+                                            int32_t value) {
     process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
-void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) {
+void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
-
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndNotACursor_ReturnsPointer) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsNotSpecifiedAndIsACursor_ReturnsTouchPad) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_X);
     mFakeEventHub->addRelativeAxis(DEVICE_ID, REL_Y);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchPad_ReturnsTouchPad) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchPad");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetSources_WhenDeviceTypeIsTouchScreen_ReturnsTouchScreen) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 }
 
 TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // Unknown key.
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
+    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
 
     // Virtual key is down.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -3501,27 +3754,26 @@
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
+    ASSERT_EQ(AKEY_STATE_UP, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME));
 }
 
 TEST_F(SingleTouchInputMapperTest, GetScanCodeState) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // Unknown key.
-    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
+    ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
 
     // Virtual key is down.
     int32_t x = toRawX(VIRTUAL_KEYS[0].centerX);
@@ -3530,40 +3782,38 @@
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+    ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 
     // Virtual key is up.
     processUp(mapper);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled());
 
-    ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
+    ASSERT_EQ(AKEY_STATE_UP, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME));
 }
 
 TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A };
     uint8_t flags[2] = { 0, 0 };
-    ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
+    ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags));
     ASSERT_TRUE(flags[0]);
     ASSERT_FALSE(flags[1]);
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3608,13 +3858,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3730,13 +3979,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3804,7 +4052,6 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDisplay) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     addConfigurationProperty("touch.displayId", VIRTUAL_DISPLAY_UNIQUE_ID);
 
@@ -3812,7 +4059,7 @@
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3903,13 +4150,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -3994,12 +4240,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenNotOrientationAware_DoesNotRotateMotions) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
     addConfigurationProperty("touch.orientationAware", "0");
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4018,11 +4263,10 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs args;
 
@@ -4084,12 +4328,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE | TOOL | DISTANCE | TILT);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -4129,13 +4372,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareLocationCalibration();
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     int32_t rawX = 100;
     int32_t rawY = 200;
@@ -4153,12 +4395,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllButtons) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -4397,12 +4638,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4533,13 +4773,12 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION);
     mFakeEventHub->addKey(DEVICE_ID, BTN_TOOL_FINGER, 0, AKEYCODE_UNKNOWN, 0);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4606,12 +4845,11 @@
 }
 
 TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsValueIsZero) {
-    SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareButtons();
     prepareAxes(POSITION | PRESSURE);
-    addMapperAndConfigure(mapper);
+    SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -4677,28 +4915,26 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
-
 // --- MultiTouchInputMapperTest ---
 
 class MultiTouchInputMapperTest : public TouchInputMapperTest {
 protected:
     void prepareAxes(int axes);
 
-    void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y);
-    void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor);
-    void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor);
-    void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor);
-    void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor);
-    void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation);
-    void processPressure(MultiTouchInputMapper* mapper, int32_t pressure);
-    void processDistance(MultiTouchInputMapper* mapper, int32_t distance);
-    void processId(MultiTouchInputMapper* mapper, int32_t id);
-    void processSlot(MultiTouchInputMapper* mapper, int32_t slot);
-    void processToolType(MultiTouchInputMapper* mapper, int32_t toolType);
-    void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value);
-    void processTimestamp(MultiTouchInputMapper* mapper, uint32_t value);
-    void processMTSync(MultiTouchInputMapper* mapper);
-    void processSync(MultiTouchInputMapper* mapper);
+    void processPosition(MultiTouchInputMapper& mapper, int32_t x, int32_t y);
+    void processTouchMajor(MultiTouchInputMapper& mapper, int32_t touchMajor);
+    void processTouchMinor(MultiTouchInputMapper& mapper, int32_t touchMinor);
+    void processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor);
+    void processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor);
+    void processOrientation(MultiTouchInputMapper& mapper, int32_t orientation);
+    void processPressure(MultiTouchInputMapper& mapper, int32_t pressure);
+    void processDistance(MultiTouchInputMapper& mapper, int32_t distance);
+    void processId(MultiTouchInputMapper& mapper, int32_t id);
+    void processSlot(MultiTouchInputMapper& mapper, int32_t slot);
+    void processToolType(MultiTouchInputMapper& mapper, int32_t toolType);
+    void processKey(MultiTouchInputMapper& mapper, int32_t code, int32_t value);
+    void processMTSync(MultiTouchInputMapper& mapper);
+    void processSync(MultiTouchInputMapper& mapper);
 };
 
 void MultiTouchInputMapperTest::prepareAxes(int axes) {
@@ -4751,87 +4987,74 @@
     }
 }
 
-void MultiTouchInputMapperTest::processPosition(
-        MultiTouchInputMapper* mapper, int32_t x, int32_t y) {
+void MultiTouchInputMapperTest::processPosition(MultiTouchInputMapper& mapper, int32_t x,
+                                                int32_t 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) {
+void MultiTouchInputMapperTest::processTouchMajor(MultiTouchInputMapper& mapper,
+                                                  int32_t touchMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MAJOR, touchMajor);
 }
 
-void MultiTouchInputMapperTest::processTouchMinor(
-        MultiTouchInputMapper* mapper, int32_t touchMinor) {
+void MultiTouchInputMapperTest::processTouchMinor(MultiTouchInputMapper& mapper,
+                                                  int32_t touchMinor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOUCH_MINOR, touchMinor);
 }
 
-void MultiTouchInputMapperTest::processToolMajor(
-        MultiTouchInputMapper* mapper, int32_t toolMajor) {
+void MultiTouchInputMapperTest::processToolMajor(MultiTouchInputMapper& mapper, int32_t toolMajor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MAJOR, toolMajor);
 }
 
-void MultiTouchInputMapperTest::processToolMinor(
-        MultiTouchInputMapper* mapper, int32_t toolMinor) {
+void MultiTouchInputMapperTest::processToolMinor(MultiTouchInputMapper& mapper, int32_t toolMinor) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_WIDTH_MINOR, toolMinor);
 }
 
-void MultiTouchInputMapperTest::processOrientation(
-        MultiTouchInputMapper* mapper, int32_t orientation) {
+void MultiTouchInputMapperTest::processOrientation(MultiTouchInputMapper& mapper,
+                                                   int32_t orientation) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_ORIENTATION, orientation);
 }
 
-void MultiTouchInputMapperTest::processPressure(
-        MultiTouchInputMapper* mapper, int32_t pressure) {
+void MultiTouchInputMapperTest::processPressure(MultiTouchInputMapper& mapper, int32_t pressure) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_PRESSURE, pressure);
 }
 
-void MultiTouchInputMapperTest::processDistance(
-        MultiTouchInputMapper* mapper, int32_t distance) {
+void MultiTouchInputMapperTest::processDistance(MultiTouchInputMapper& mapper, int32_t distance) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_DISTANCE, distance);
 }
 
-void MultiTouchInputMapperTest::processId(
-        MultiTouchInputMapper* mapper, int32_t id) {
+void MultiTouchInputMapperTest::processId(MultiTouchInputMapper& mapper, int32_t id) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TRACKING_ID, id);
 }
 
-void MultiTouchInputMapperTest::processSlot(
-        MultiTouchInputMapper* mapper, int32_t slot) {
+void MultiTouchInputMapperTest::processSlot(MultiTouchInputMapper& mapper, int32_t slot) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_SLOT, slot);
 }
 
-void MultiTouchInputMapperTest::processToolType(
-        MultiTouchInputMapper* mapper, int32_t toolType) {
+void MultiTouchInputMapperTest::processToolType(MultiTouchInputMapper& mapper, int32_t toolType) {
     process(mapper, ARBITRARY_TIME, EV_ABS, ABS_MT_TOOL_TYPE, toolType);
 }
 
-void MultiTouchInputMapperTest::processKey(
-        MultiTouchInputMapper* mapper, int32_t code, int32_t value) {
+void MultiTouchInputMapperTest::processKey(MultiTouchInputMapper& mapper, int32_t code,
+                                           int32_t value) {
     process(mapper, ARBITRARY_TIME, EV_KEY, code, value);
 }
 
-void MultiTouchInputMapperTest::processTimestamp(MultiTouchInputMapper* mapper, uint32_t value) {
-    process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, value);
-}
-
-void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) {
+void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0);
 }
 
-void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) {
+void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper& mapper) {
     process(mapper, ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
 }
 
-
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5103,12 +5326,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5279,12 +5501,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     prepareVirtualKeys();
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
 
@@ -5450,11 +5671,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR | DISTANCE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5500,12 +5720,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL | MINOR);
     addConfigurationProperty("touch.size.calibration", "geometric");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5538,7 +5757,6 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_SummedLinearCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
@@ -5546,7 +5764,7 @@
     addConfigurationProperty("touch.size.scale", "10");
     addConfigurationProperty("touch.size.bias", "160");
     addConfigurationProperty("touch.size.isSummed", "1");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     // Note: We only provide a single common touch/tool value because the device is assumed
@@ -5591,14 +5809,13 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_AreaCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | TOUCH | TOOL);
     addConfigurationProperty("touch.size.calibration", "area");
     addConfigurationProperty("touch.size.scale", "43");
     addConfigurationProperty("touch.size.bias", "3");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // These calculations are based on the input device calibration documentation.
     int32_t rawX = 100;
@@ -5625,16 +5842,15 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_PressureAxis_AmplitudeCalibration) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | PRESSURE);
     addConfigurationProperty("touch.pressure.calibration", "amplitude");
     addConfigurationProperty("touch.pressure.scale", "0.01");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     InputDeviceInfo info;
-    mapper->populateDeviceInfo(&info);
+    mapper.populateDeviceInfo(&info);
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
             AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TOUCHSCREEN,
             0.0f, RAW_PRESSURE_MAX * 0.01, 0.0f, 0.0f));
@@ -5660,11 +5876,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllButtons) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     NotifyKeyArgs keyArgs;
@@ -5904,11 +6119,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleAllToolTypes) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6055,12 +6269,11 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenBtnTouchPresent_HoversIfItsValueIsZero) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT);
     mFakeEventHub->addKey(DEVICE_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6127,11 +6340,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfItsValueIsZero) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION | ID | SLOT | PRESSURE);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
 
@@ -6198,71 +6410,12 @@
             toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0));
 }
 
-TEST_F(MultiTouchInputMapperTest, Process_HandlesTimestamp) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
-    NotifyMotionArgs args;
-
-    // By default, deviceTimestamp should be zero
-    processPosition(mapper, 100, 100);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(0U, args.deviceTimestamp);
-
-    // Now the timestamp of 1000 is reported by evdev and should appear in MotionArgs
-    processPosition(mapper, 0, 0);
-    processTimestamp(mapper, 1000);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(1000U, args.deviceTimestamp);
-}
-
-TEST_F(MultiTouchInputMapperTest, WhenMapperIsReset_TimestampIsCleared) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
-
-    addConfigurationProperty("touch.deviceType", "touchScreen");
-    prepareDisplay(DISPLAY_ORIENTATION_0);
-    prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
-    NotifyMotionArgs args;
-
-    // Send a touch event with a timestamp
-    processPosition(mapper, 100, 100);
-    processTimestamp(mapper, 1);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(1U, args.deviceTimestamp);
-
-    // Since the data accumulates, and new timestamp has not arrived, deviceTimestamp won't change
-    processPosition(mapper, 100, 200);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    ASSERT_EQ(1U, args.deviceTimestamp);
-
-    mapper->reset(/* when */ 0);
-    // After the mapper is reset, deviceTimestamp should become zero again
-    processPosition(mapper, 100, 300);
-    processMTSync(mapper);
-    processSync(mapper);
-    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
-    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;
@@ -6271,7 +6424,7 @@
 
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
     mFakePolicy->addInputPortAssociation(usb2, hdmi2);
@@ -6302,14 +6455,13 @@
  * 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);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
-    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources());
 
     NotifyMotionArgs motionArgs;
 
@@ -6329,21 +6481,22 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
-    // Setup PointerController for second display.
+    // Setup for second display.
     sp<FakePointerController> fakePointerController = new FakePointerController();
-    fakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+    fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
     fakePointerController->setPosition(100, 200);
     fakePointerController->setButtonState(0);
-    fakePointerController->setDisplayId(SECONDARY_DISPLAY_ID);
     mFakePolicy->setPointerController(mDevice->getId(), fakePointerController);
 
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
+    mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+    prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL);
+
     prepareDisplay(DISPLAY_ORIENTATION_0);
     prepareAxes(POSITION);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Check source is mouse that would obtain the PointerController.
-    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
 
     NotifyMotionArgs motionArgs;
     processPosition(mapper, 100, 100);
@@ -6356,19 +6509,19 @@
 
 TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
     // Setup the first touch screen device.
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION | ID | SLOT);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     // Create the second touch screen device, and enable multi fingers.
     const std::string USB2 = "USB2";
-    const int32_t SECOND_DEVICE_ID = 2;
+    constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
     InputDeviceIdentifier identifier;
-    identifier.name = DEVICE_NAME;
+    identifier.name = "TOUCHSCREEN2";
     identifier.location = USB2;
-    InputDevice* device2 = new InputDevice(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
-            DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
+    std::unique_ptr<InputDevice> device2 =
+            std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION,
+                                          DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
     mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/);
     mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
             0 /*flat*/, 0 /*fuzz*/);
@@ -6383,8 +6536,7 @@
             String8("touchScreen"));
 
     // Setup the second touch screen device.
-    MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2);
-    device2->addMapper(mapper2);
+    MultiTouchInputMapper& mapper2 = device2->addMapper<MultiTouchInputMapper>();
     device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/);
     device2->reset(ARBITRARY_TIME);
 
@@ -6436,11 +6588,10 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
     prepareDisplay(DISPLAY_ORIENTATION_0);
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
 
     NotifyMotionArgs motionArgs;
     // Unrotated video frame
@@ -6462,10 +6613,9 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     // Unrotated video frame
     TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
     NotifyMotionArgs motionArgs;
@@ -6487,10 +6637,9 @@
 }
 
 TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
-    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
     prepareAxes(POSITION);
     addConfigurationProperty("touch.deviceType", "touchScreen");
-    addMapperAndConfigure(mapper);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
     // Unrotated video frames. There's no rule that they must all have the same dimensions,
     // so mix these.
     TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
@@ -6509,4 +6658,169 @@
     ASSERT_EQ(frames, motionArgs.videoFrames);
 }
 
+/**
+ * If we had defined port associations, but the viewport is not ready, the touch device would be
+ * expected to be disabled, and it should be enabled after the viewport has found.
+ */
+TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) {
+    constexpr uint8_t hdmi2 = 1;
+    const std::string secondaryUniqueId = "uniqueId2";
+    constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL;
+
+    mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2);
+
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    ASSERT_EQ(mDevice->isEnabled(), false);
+
+    // Add display on hdmi2, the device should be enabled and can receive touch event.
+    prepareSecondaryDisplay(type, hdmi2);
+    ASSERT_EQ(mDevice->isEnabled(), true);
+
+    // Send a touch event.
+    processPosition(mapper, 100, 100);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+    ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId);
+}
+
+/**
+ * Test touch should not work if outside of surface.
+ */
+TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    // Touch on left-top area should work.
+    int32_t rawX = DISPLAY_WIDTH / 2 - 1;
+    int32_t rawY = DISPLAY_HEIGHT / 2 - 1;
+    processPosition(mapper, rawX, rawY);
+    processSync(mapper);
+
+    NotifyMotionArgs args;
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
+
+    // Reset.
+    mapper.reset(ARBITRARY_TIME);
+
+    // Let logical display be different to physical display and rotate 90-degrees.
+    std::optional<DisplayViewport> internalViewport =
+            mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL);
+    internalViewport->orientation = DISPLAY_ORIENTATION_90;
+    internalViewport->logicalLeft = 0;
+    internalViewport->logicalTop = 0;
+    internalViewport->logicalRight = DISPLAY_HEIGHT;
+    internalViewport->logicalBottom = DISPLAY_WIDTH / 2;
+
+    internalViewport->physicalLeft = DISPLAY_HEIGHT;
+    internalViewport->physicalTop = DISPLAY_WIDTH / 2;
+    internalViewport->physicalRight = DISPLAY_HEIGHT;
+    internalViewport->physicalBottom = DISPLAY_WIDTH;
+
+    internalViewport->deviceWidth = DISPLAY_HEIGHT;
+    internalViewport->deviceHeight = DISPLAY_WIDTH;
+    mFakePolicy->updateViewport(internalViewport.value());
+    configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+
+    // Display align to right-top after rotate 90-degrees, touch on left-top area should not work.
+    processPosition(mapper, rawX, rawY);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandleSingleTouch) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+    // finger down
+    processId(mapper, 1);
+    processPosition(mapper, x1, y1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // finger move
+    processId(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // finger up.
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // new finger down
+    processId(mapper, 1);
+    processPosition(mapper, x3, y3);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+}
+
+/**
+ * Test touch should be canceled when received the MT_TOOL_PALM event, and the following MOVE and
+ * UP events should be ignored.
+ */
+TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType) {
+    addConfigurationProperty("touch.deviceType", "touchScreen");
+    prepareDisplay(DISPLAY_ORIENTATION_0);
+    prepareAxes(POSITION | ID | SLOT | TOOL_TYPE);
+    MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>();
+
+    NotifyMotionArgs motionArgs;
+
+    // default tool type is finger
+    constexpr int32_t x1 = 100, y1 = 200, x2 = 120, y2 = 220, x3 = 140, y3 = 240;
+    processId(mapper, 1);
+    processPosition(mapper, x1, y1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // Tool changed to MT_TOOL_PALM expect sending the cancel event.
+    processToolType(mapper, MT_TOOL_PALM);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action);
+
+    // Ignore the following MOVE and UP events if had detect a palm event.
+    processId(mapper, 1);
+    processPosition(mapper, x2, y2);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // finger up.
+    processId(mapper, -1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+    // new finger down
+    processToolType(mapper, MT_TOOL_FINGER);
+    processId(mapper, 1);
+    processPosition(mapper, x3, y3);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+}
+
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 3ee33f1..06b05ac 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -19,6 +19,15 @@
 
 #include "TestInputListener.h"
 
+namespace {
+
+using std::chrono_literals::operator""ms;
+
+// Timeout for waiting for an expected event
+static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms;
+
+} // namespace
+
 namespace android {
 
 // --- TestInputListener ---
@@ -29,87 +38,117 @@
 
 void TestInputListener::assertNotifyConfigurationChangedWasCalled(
         NotifyConfigurationChangedArgs* outEventArgs) {
-    ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
-            << "Expected notifyConfigurationChanged() to have been called.";
-    if (outEventArgs) {
-        *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
-    }
-    mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifyConfigurationChangedArgs>(outEventArgs,
+                                                         "Expected notifyConfigurationChanged() "
+                                                         "to have been called."));
 }
 
 void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
-    ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
-            << "Expected notifyConfigurationChanged() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyConfigurationChangedArgs>(
+            "notifyConfigurationChanged() should not be 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());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<
+                    NotifyDeviceResetArgs>(outEventArgs,
+                                           "Expected notifyDeviceReset() to have been called."));
 }
 
 void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
-    ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
-            << "Expected notifyDeviceReset() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(
+            assertNotCalled<NotifyDeviceResetArgs>("notifyDeviceReset() should not be 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());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
 }
 
 void TestInputListener::assertNotifyKeyWasNotCalled() {
-    ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be 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());
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifyMotionArgs>(outEventArgs,
+                                           "Expected notifyMotion() to have been called."));
 }
 
 void TestInputListener::assertNotifyMotionWasNotCalled() {
-    ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
-            << "Expected notifyMotion() to not have been called.";
+    ASSERT_NO_FATAL_FAILURE(
+            assertNotCalled<NotifySwitchArgs>("notifySwitch() should not be called."));
 }
 
 void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
-    ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
-            << "Expected notifySwitch() to have been called.";
-    if (outEventArgs) {
-        *outEventArgs = *mNotifySwitchArgsQueue.begin();
+    ASSERT_NO_FATAL_FAILURE(
+            assertCalled<NotifySwitchArgs>(outEventArgs,
+                                           "Expected notifySwitch() to have been called."));
+}
+
+template <class NotifyArgsType>
+void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
+    std::unique_lock<std::mutex> lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+    if (queue.empty()) {
+        const bool eventReceived =
+                mCondition.wait_for(lock, WAIT_TIMEOUT,
+                                    [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+        if (!eventReceived) {
+            FAIL() << "Timed out waiting for event: " << message.c_str();
+        }
     }
-    mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());
+    if (outEventArgs) {
+        *outEventArgs = *queue.begin();
+    }
+    queue.erase(queue.begin());
+}
+
+template <class NotifyArgsType>
+void TestInputListener::assertNotCalled(std::string message) {
+    std::unique_lock<std::mutex> lock(mLock);
+    base::ScopedLockAssertion assumeLocked(mLock);
+
+    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+    const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) {
+        return !queue.empty();
+    });
+    if (eventReceived) {
+        FAIL() << "Unexpected event: " << message.c_str();
+    }
+}
+
+template <class NotifyArgsType>
+void TestInputListener::notify(const NotifyArgsType* args) {
+    std::scoped_lock<std::mutex> lock(mLock);
+
+    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
+    queue.push_back(*args);
+    mCondition.notify_all();
 }
 
 void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
-    mNotifyConfigurationChangedArgsQueue.push_back(*args);
+    notify<NotifyConfigurationChangedArgs>(args);
 }
 
 void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
-    mNotifyDeviceResetArgsQueue.push_back(*args);
+    notify<NotifyDeviceResetArgs>(args);
 }
 
 void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
-    mNotifyKeyArgsQueue.push_back(*args);
+    notify<NotifyKeyArgs>(args);
 }
 
 void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
-    mNotifyMotionArgsQueue.push_back(*args);
+    notify<NotifyMotionArgs>(args);
 }
 
 void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
-        mNotifySwitchArgsQueue.push_back(*args);
-    }
-
+    notify<NotifySwitchArgs>(args);
+}
 
 } // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 085d343..945e2ea 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -17,6 +17,7 @@
 #ifndef _UI_TEST_INPUT_LISTENER_H
 #define _UI_TEST_INPUT_LISTENER_H
 
+#include <android-base/thread_annotations.h>
 #include <gtest/gtest.h>
 #include "InputListener.h"
 
@@ -25,13 +26,6 @@
 // --- 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();
 
@@ -58,15 +52,34 @@
     void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);
 
 private:
-    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
+    template <class NotifyArgsType>
+    void assertCalled(NotifyArgsType* outEventArgs, std::string message);
 
-    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
+    template <class NotifyArgsType>
+    void assertNotCalled(std::string message);
 
-    virtual void notifyKey(const NotifyKeyArgs* args);
+    template <class NotifyArgsType>
+    void notify(const NotifyArgsType* args);
 
-    virtual void notifyMotion(const NotifyMotionArgs* args);
+    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
 
-    virtual void notifySwitch(const NotifySwitchArgs* args);
+    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
+
+    virtual void notifyKey(const NotifyKeyArgs* args) override;
+
+    virtual void notifyMotion(const NotifyMotionArgs* args) override;
+
+    virtual void notifySwitch(const NotifySwitchArgs* args) override;
+
+    std::mutex mLock;
+    std::condition_variable mCondition;
+
+    std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
+               std::vector<NotifyDeviceResetArgs>,          //
+               std::vector<NotifyKeyArgs>,                  //
+               std::vector<NotifyMotionArgs>,               //
+               std::vector<NotifySwitchArgs>>               //
+            mQueues GUARDED_BY(mLock);
 };
 
 } // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
new file mode 100644
index 0000000..2775d21
--- /dev/null
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "UinputDevice.h"
+
+#include <android-base/stringprintf.h>
+
+namespace android {
+
+// --- UinputDevice ---
+
+UinputDevice::UinputDevice(const char* name) : mName(name) {}
+
+UinputDevice::~UinputDevice() {
+    if (ioctl(mDeviceFd, UI_DEV_DESTROY)) {
+        ALOGE("Error while destroying uinput device: %s", strerror(errno));
+    }
+    mDeviceFd.reset();
+}
+
+void UinputDevice::init() {
+    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+    if (mDeviceFd < 0) {
+        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
+    }
+
+    struct uinput_user_dev device = {};
+    strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE);
+    device.id.bustype = BUS_USB;
+    device.id.vendor = 0x01;
+    device.id.product = 0x01;
+    device.id.version = 1;
+
+    // Using EXPECT instead of ASSERT to allow the device creation to continue even when
+    // some failures are reported when configuring the device.
+    EXPECT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device));
+
+    if (write(mDeviceFd, &device, sizeof(device)) < 0) {
+        FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
+               << strerror(errno);
+    }
+
+    if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
+        FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
+    }
+}
+
+void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+    struct input_event event = {};
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    event.time = {}; // uinput ignores the timestamp
+
+    if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
+        std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16
+                                             " with value %" PRId32 " : %s",
+                                             type, code, value, strerror(errno));
+        ALOGE("%s", msg.c_str());
+        ADD_FAILURE() << msg.c_str();
+    }
+}
+
+// --- UinputKeyboard ---
+
+UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys)
+      : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {}
+
+void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) {
+    // enable key press/release event
+    if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
+    }
+
+    // enable set of KEY events
+    std::for_each(mKeys.begin(), mKeys.end(), [fd](int key) {
+        if (ioctl(fd, UI_SET_KEYBIT, key)) {
+            ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno);
+        }
+    });
+
+    // enable synchronization event
+    if (ioctl(fd, UI_SET_EVBIT, EV_SYN)) {
+        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
+    }
+}
+
+void UinputKeyboard::pressKey(int key) {
+    if (mKeys.find(key) == mKeys.end()) {
+        ADD_FAILURE() << mName << ": Cannot inject key press: Key not found: " << key;
+    }
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 1));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputKeyboard::releaseKey(int key) {
+    if (mKeys.find(key) == mKeys.end()) {
+        ADD_FAILURE() << mName << ": Cannot inject key release: Key not found: " << key;
+    }
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 0));
+    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputKeyboard::pressAndReleaseKey(int key) {
+    EXPECT_NO_FATAL_FAILURE(pressKey(key));
+    EXPECT_NO_FATAL_FAILURE(releaseKey(key));
+}
+
+// --- UinputHomeKey---
+
+UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
+
+void UinputHomeKey::pressAndReleaseHomeKey() {
+    EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
new file mode 100644
index 0000000..57d9011
--- /dev/null
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_UINPUT_INJECTOR_H
+#define _UI_TEST_INPUT_UINPUT_INJECTOR_H
+
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+
+#include <memory>
+
+namespace android {
+
+// This is the factory method that must be used to create a UinputDevice.
+template <class D, class... Ts>
+std::unique_ptr<D> createUinputDevice(Ts... args) {
+    // Using `new` to access non-public constructors.
+    std::unique_ptr<D> dev(new D(&args...));
+    EXPECT_NO_FATAL_FAILURE(dev->init());
+    return dev;
+}
+
+// --- UinputDevice ---
+
+class UinputDevice {
+public:
+    virtual ~UinputDevice();
+
+    inline const char* getName() const { return mName; }
+
+    // Subclasses must either provide a public constructor or must be-friend the factory method.
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+protected:
+    const char* mName;
+
+    UinputDevice(const char* name);
+
+    // Signals which types of events this device supports before it is created.
+    // This must be overridden by subclasses.
+    virtual void configureDevice(int fd, uinput_user_dev* device) = 0;
+
+    void injectEvent(uint16_t type, uint16_t code, int32_t value);
+
+private:
+    base::unique_fd mDeviceFd;
+
+    // This is called once by the factory method createUinputDevice().
+    void init();
+};
+
+// --- UinputKeyboard ---
+
+class UinputKeyboard : public UinputDevice {
+public:
+    static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device";
+
+    // Injects key press and sync.
+    void pressKey(int key);
+    // Injects key release and sync.
+    void releaseKey(int key);
+    // Injects 4 events: key press, sync, key release, and sync.
+    void pressAndReleaseKey(int key);
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+protected:
+    UinputKeyboard(std::initializer_list<int> keys = {});
+
+private:
+    void configureDevice(int fd, uinput_user_dev* device) override;
+
+    std::set<int> mKeys;
+};
+
+// --- UinputHomeKey---
+
+// A keyboard device that has a single HOME key.
+class UinputHomeKey : public UinputKeyboard {
+public:
+    // Injects 4 events: key press, sync, key release, and sync.
+    void pressAndReleaseHomeKey();
+
+    template <class D, class... Ts>
+    friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+private:
+    UinputHomeKey();
+};
+
+} // namespace android
+
+#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 1c9a4af..5246c78 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -42,6 +42,7 @@
         "libbinder",
         "libsensor",
         "libsensorprivacy",
+        "libprotoutil",
         "libcrypto",
         "libbase",
         "libhidlbase",
@@ -52,6 +53,8 @@
 
     static_libs: ["android.hardware.sensors@1.0-convert"],
 
+    generated_headers: ["framework-cppstream-protos"],
+
     // our public headers depend on libsensor and libsensorprivacy
     export_shared_lib_headers: ["libsensor", "libsensorprivacy"],
 }
diff --git a/services/sensorservice/RecentEventLogger.cpp b/services/sensorservice/RecentEventLogger.cpp
index 207b097..d7ca6e1 100644
--- a/services/sensorservice/RecentEventLogger.cpp
+++ b/services/sensorservice/RecentEventLogger.cpp
@@ -17,6 +17,8 @@
 #include "RecentEventLogger.h"
 #include "SensorServiceUtils.h"
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <utils/Timers.h>
 
 #include <inttypes.h>
@@ -84,6 +86,40 @@
     return std::string(buffer.string());
 }
 
+/**
+ * Dump debugging information as android.service.SensorEventsProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void RecentEventLogger::dump(util::ProtoOutputStream* proto) const {
+    using namespace service::SensorEventsProto;
+    std::lock_guard<std::mutex> lk(mLock);
+
+    proto->write(RecentEventsLog::RECENT_EVENTS_COUNT, int(mRecentEvents.size()));
+    for (int i = mRecentEvents.size() - 1; i >= 0; --i) {
+        const auto& ev = mRecentEvents[i];
+        const uint64_t token = proto->start(RecentEventsLog::EVENTS);
+        proto->write(Event::TIMESTAMP_SEC, float(ev.mEvent.timestamp) / 1e9f);
+        proto->write(Event::WALL_TIMESTAMP_MS, ev.mWallTime.tv_sec * 1000LL
+                + ns2ms(ev.mWallTime.tv_nsec));
+
+        if (mMaskData) {
+            proto->write(Event::MASKED, true);
+        } else {
+            if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
+                proto->write(Event::INT64_DATA, int64_t(ev.mEvent.u64.step_counter));
+            } else {
+                for (size_t k = 0; k < mEventSize; ++k) {
+                    proto->write(Event::FLOAT_ARRAY, ev.mEvent.data[k]);
+                }
+            }
+        }
+        proto->end(token);
+    }
+}
+
 void RecentEventLogger::setFormat(std::string format) {
     if (format == "mask_data" ) {
         mMaskData = true;
diff --git a/services/sensorservice/RecentEventLogger.h b/services/sensorservice/RecentEventLogger.h
index 67378b7..3a2ae77 100644
--- a/services/sensorservice/RecentEventLogger.h
+++ b/services/sensorservice/RecentEventLogger.h
@@ -48,6 +48,7 @@
 
     // Dumpable interface
     virtual std::string dump() const override;
+    virtual void dump(util::ProtoOutputStream* proto) const override;
     virtual void setFormat(std::string format) override;
 
 protected:
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 717f317..33f940f 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -21,6 +21,8 @@
 #include "SensorService.h"
 
 #include <android-base/logging.h>
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <sensors/convert.h>
 #include <cutils/atomic.h>
 #include <utils/Errors.h>
@@ -39,6 +41,7 @@
 using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
+using android::util::ProtoOutputStream;
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -134,7 +137,12 @@
 
                     mActivationCount.add(list[i].sensorHandle, model);
 
-                    checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+                    // Only disable all sensors on HAL 1.0 since HAL 2.0
+                    // handles this in its initialize method
+                    if (!mSensors->supportsMessageQueues()) {
+                        checkReturn(mSensors->activate(list[i].sensorHandle,
+                                    0 /* enabled */));
+                    }
                 }
             }));
 }
@@ -391,6 +399,43 @@
     return result.string();
 }
 
+/**
+ * Dump debugging information as android.service.SensorDeviceProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorDevice::dump(ProtoOutputStream* proto) const {
+    using namespace service::SensorDeviceProto;
+    if (mSensors == nullptr) {
+        proto->write(INITIALIZED , false);
+        return;
+    }
+    proto->write(INITIALIZED , true);
+    proto->write(TOTAL_SENSORS , int(mSensorList.size()));
+    proto->write(ACTIVE_SENSORS , int(mActivationCount.size()));
+
+    Mutex::Autolock _l(mLock);
+    for (const auto & s : mSensorList) {
+        int32_t handle = s.handle;
+        const Info& info = mActivationCount.valueFor(handle);
+        if (info.numActiveClients() == 0) continue;
+
+        uint64_t token = proto->start(SENSORS);
+        proto->write(SensorProto::HANDLE , handle);
+        proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size()));
+        for (size_t j = 0; j < info.batchParams.size(); j++) {
+            const BatchParams& params = info.batchParams[j];
+            proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f);
+            proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f);
+        }
+        proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f);
+        proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f);
+        proto->end(token);
+    }
+}
+
 ssize_t SensorDevice::getSensorList(sensor_t const** list) {
     *list = &mSensorList[0];
 
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index d2c6994..33aa7d6 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -123,7 +123,8 @@
     bool isSensorActive(int handle) const;
 
     // Dumpable
-    virtual std::string dump() const;
+    virtual std::string dump() const override;
+    virtual void dump(util::ProtoOutputStream* proto) const override;
 private:
     friend class Singleton<SensorDevice>;
 
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index cd0ea5d..106efd6 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -16,12 +16,16 @@
 
 #include "SensorDevice.h"
 #include "SensorDirectConnection.h"
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
 
 #define UNUSED(x) (void)(x)
 
 namespace android {
 
+using util::ProtoOutputStream;
+
 SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service,
         uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle,
         const String16& opPackageName)
@@ -64,6 +68,27 @@
     }
 }
 
+/**
+ * Dump debugging information as android.service.SensorDirectConnectionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorService::SensorDirectConnection::dump(ProtoOutputStream* proto) const {
+    using namespace service::SensorDirectConnectionProto;
+    Mutex::Autolock _l(mConnectionLock);
+    proto->write(PACKAGE_NAME, std::string(String8(mOpPackageName).string()));
+    proto->write(HAL_CHANNEL_HANDLE, getHalChannelHandle());
+    proto->write(NUM_SENSOR_ACTIVATED, int(mActivated.size()));
+    for (auto &i : mActivated) {
+        uint64_t token = proto->start(SENSORS);
+        proto->write(SensorProto::SENSOR, i.first);
+        proto->write(SensorProto::RATE, i.second);
+        proto->end(token);
+    }
+}
+
 sp<BitTube> SensorService::SensorDirectConnection::getSensorChannel() const {
     return nullptr;
 }
diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h
index 5c398a8..ead08d3 100644
--- a/services/sensorservice/SensorDirectConnection.h
+++ b/services/sensorservice/SensorDirectConnection.h
@@ -40,6 +40,7 @@
             const sensors_direct_mem_t *mem, int32_t halChannelHandle,
             const String16& opPackageName);
     void dump(String8& result) const;
+    void dump(util::ProtoOutputStream* proto) const;
     uid_t getUid() const { return mUid; }
     int32_t getHalChannelHandle() const;
     bool isEquivalent(const sensors_direct_mem_t *mem) const;
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index 0e40940..9a13c00 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -17,6 +17,8 @@
 #include <sys/socket.h>
 #include <utils/threads.h>
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <sensor/SensorEventQueue.h>
 
 #include "vec.h"
@@ -110,6 +112,51 @@
 #endif
 }
 
+/**
+ * Dump debugging information as android.service.SensorEventConnectionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorService::SensorEventConnection::dump(util::ProtoOutputStream* proto) const {
+    using namespace service::SensorEventConnectionProto;
+    Mutex::Autolock _l(mConnectionLock);
+
+    if (!mService->isWhiteListedPackage(getPackageName())) {
+        proto->write(OPERATING_MODE, OP_MODE_RESTRICTED);
+    } else if (mDataInjectionMode) {
+        proto->write(OPERATING_MODE, OP_MODE_DATA_INJECTION);
+    } else {
+        proto->write(OPERATING_MODE, OP_MODE_NORMAL);
+    }
+    proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+    proto->write(WAKE_LOCK_REF_COUNT, int32_t(mWakeLockRefCount));
+    proto->write(UID, int32_t(mUid));
+    proto->write(CACHE_SIZE, int32_t(mCacheSize));
+    proto->write(MAX_CACHE_SIZE, int32_t(mMaxCacheSize));
+    for (size_t i = 0; i < mSensorInfo.size(); ++i) {
+        const FlushInfo& flushInfo = mSensorInfo.valueAt(i);
+        const uint64_t token = proto->start(FLUSH_INFOS);
+        proto->write(FlushInfoProto::SENSOR_NAME,
+                std::string(mService->getSensorName(mSensorInfo.keyAt(i))));
+        proto->write(FlushInfoProto::SENSOR_HANDLE, mSensorInfo.keyAt(i));
+        proto->write(FlushInfoProto::FIRST_FLUSH_PENDING, flushInfo.mFirstFlushPending);
+        proto->write(FlushInfoProto::PENDING_FLUSH_EVENTS_TO_SEND,
+                flushInfo.mPendingFlushEventsToSend);
+        proto->end(token);
+    }
+#if DEBUG_CONNECTIONS
+    proto->write(EVENTS_RECEIVED, mEventsReceived);
+    proto->write(EVENTS_SENT, mEventsSent);
+    proto->write(EVENTS_CACHE, mEventsSentFromCache);
+    proto->write(EVENTS_DROPPED, mEventsReceived - (mEventsSentFromCache + mEventsSent +
+            mCacheSize));
+    proto->write(TOTAL_ACKS_NEEDED, mTotalAcksNeeded);
+    proto->write(TOTAL_ACKS_RECEIVED, mTotalAcksReceived);
+#endif
+}
+
 bool SensorService::SensorEventConnection::addSensor(int32_t handle) {
     Mutex::Autolock _l(mConnectionLock);
     sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle);
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index fd881cb..caf5d7c 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -62,6 +62,7 @@
     bool removeSensor(int32_t handle);
     void setFirstFlushPending(int32_t handle, bool value);
     void dump(String8& result);
+    void dump(util::ProtoOutputStream* proto) const;
     bool needsWakeLock();
     void resetWakeLockRefCount();
     String8 getPackageName() const;
diff --git a/services/sensorservice/SensorFusion.cpp b/services/sensorservice/SensorFusion.cpp
index 414f673..e27b52b 100644
--- a/services/sensorservice/SensorFusion.cpp
+++ b/services/sensorservice/SensorFusion.cpp
@@ -18,6 +18,9 @@
 #include "SensorFusion.h"
 #include "SensorService.h"
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
+
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -183,7 +186,7 @@
     return mAcc.getMinDelay();
 }
 
-void SensorFusion::dump(String8& result) {
+void SensorFusion::dump(String8& result) const {
     const Fusion& fusion_9axis(mFusions[FUSION_9AXIS]);
     result.appendFormat("9-axis fusion %s (%zd clients), gyro-rate=%7.2fHz, "
             "q=< %g, %g, %g, %g > (%g), "
@@ -235,5 +238,42 @@
             fusion_nogyro.getBias().z);
 }
 
+void SensorFusion::dumpFusion(FUSION_MODE mode, util::ProtoOutputStream* proto) const {
+    using namespace service::SensorFusionProto::FusionProto;
+    const Fusion& fusion(mFusions[mode]);
+    proto->write(ENABLED, mEnabled[mode]);
+    proto->write(NUM_CLIENTS, (int)mClients[mode].size());
+    proto->write(ESTIMATED_GYRO_RATE, mEstimatedGyroRate);
+    proto->write(ATTITUDE_X, fusion.getAttitude().x);
+    proto->write(ATTITUDE_Y, fusion.getAttitude().y);
+    proto->write(ATTITUDE_Z, fusion.getAttitude().z);
+    proto->write(ATTITUDE_W, fusion.getAttitude().w);
+    proto->write(ATTITUDE_LENGTH, length(fusion.getAttitude()));
+    proto->write(BIAS_X, fusion.getBias().x);
+    proto->write(BIAS_Y, fusion.getBias().y);
+    proto->write(BIAS_Z, fusion.getBias().z);
+}
+
+/**
+ * Dump debugging information as android.service.SensorFusionProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorFusion::dump(util::ProtoOutputStream* proto) const {
+    uint64_t token = proto->start(service::SensorFusionProto::FUSION_9AXIS);
+    dumpFusion(FUSION_9AXIS, proto);
+    proto->end(token);
+
+    token = proto->start(service::SensorFusionProto::FUSION_NOMAG);
+    dumpFusion(FUSION_NOMAG, proto);
+    proto->end(token);
+
+    token = proto->start(service::SensorFusionProto::FUSION_NOGYRO);
+    dumpFusion(FUSION_NOGYRO, proto);
+    proto->end(token);
+}
+
 // ---------------------------------------------------------------------------
 }; // namespace android
diff --git a/services/sensorservice/SensorFusion.h b/services/sensorservice/SensorFusion.h
index 8c0fbf9..66a7290 100644
--- a/services/sensorservice/SensorFusion.h
+++ b/services/sensorservice/SensorFusion.h
@@ -90,7 +90,9 @@
     float getPowerUsage(int mode=FUSION_9AXIS) const;
     int32_t getMinDelay() const;
 
-    void dump(String8& result);
+    void dump(String8& result) const;
+    void dump(util::ProtoOutputStream* proto) const;
+    void dumpFusion(FUSION_MODE mode, util::ProtoOutputStream* proto) const;
 };
 
 
diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp
index aa306d8..0ce32cc 100644
--- a/services/sensorservice/SensorList.cpp
+++ b/services/sensorservice/SensorList.cpp
@@ -16,6 +16,8 @@
 
 #include "SensorList.h"
 
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <hardware/sensors.h>
 #include <utils/String8.h>
 
@@ -203,6 +205,64 @@
     return std::string(result.string());
 }
 
+/**
+ * Dump debugging information as android.service.SensorListProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+void SensorList::dump(util::ProtoOutputStream* proto) const {
+    using namespace service::SensorListProto;
+    using namespace service::SensorListProto::SensorProto;
+
+    forEachSensor([&proto] (const Sensor& s) -> bool {
+        const uint64_t token = proto->start(SENSORS);
+        proto->write(HANDLE, s.getHandle());
+        proto->write(NAME, std::string(s.getName().string()));
+        proto->write(VENDOR, std::string(s.getVendor().string()));
+        proto->write(VERSION, s.getVersion());
+        proto->write(STRING_TYPE, std::string(s.getStringType().string()));
+        proto->write(TYPE, s.getType());
+        proto->write(REQUIRED_PERMISSION, std::string(s.getRequiredPermission().size() ?
+                s.getRequiredPermission().string() : ""));
+        proto->write(FLAGS, int(s.getFlags()));
+        switch (s.getReportingMode()) {
+            case AREPORTING_MODE_CONTINUOUS:
+                proto->write(REPORTING_MODE, RM_CONTINUOUS);
+                break;
+            case AREPORTING_MODE_ON_CHANGE:
+                proto->write(REPORTING_MODE, RM_ON_CHANGE);
+                break;
+            case AREPORTING_MODE_ONE_SHOT:
+                proto->write(REPORTING_MODE, RM_ONE_SHOT);
+                break;
+            case AREPORTING_MODE_SPECIAL_TRIGGER:
+                proto->write(REPORTING_MODE, RM_SPECIAL_TRIGGER);
+                break;
+            default:
+                proto->write(REPORTING_MODE, RM_UNKNOWN);
+        }
+        proto->write(MAX_DELAY_US, s.getMaxDelay());
+        proto->write(MIN_DELAY_US, s.getMinDelay());
+        proto->write(FIFO_MAX_EVENT_COUNT, int(s.getFifoMaxEventCount()));
+        proto->write(FIFO_RESERVED_EVENT_COUNT, int(s.getFifoReservedEventCount()));
+        proto->write(IS_WAKEUP, s.isWakeUpSensor());
+        proto->write(DATA_INJECTION_SUPPORTED, s.isDataInjectionSupported());
+        proto->write(IS_DYNAMIC, s.isDynamicSensor());
+        proto->write(HAS_ADDITIONAL_INFO, s.hasAdditionalInfo());
+        proto->write(HIGHEST_RATE_LEVEL, s.getHighestDirectReportRateLevel());
+        proto->write(ASHMEM, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM));
+        proto->write(GRALLOC, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC));
+        proto->write(MIN_VALUE, s.getMinValue());
+        proto->write(MAX_VALUE, s.getMaxValue());
+        proto->write(RESOLUTION, s.getResolution());
+        proto->write(POWER_USAGE, s.getPowerUsage());
+        proto->end(token);
+        return true;
+    });
+}
+
 SensorList::~SensorList() {
 }
 
diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h
index 6b90ad9..8424b22 100644
--- a/services/sensorservice/SensorList.h
+++ b/services/sensorservice/SensorList.h
@@ -71,6 +71,7 @@
 
     // Dumpable interface
     virtual std::string dump() const override;
+    virtual void dump(util::ProtoOutputStream* proto) const override;
 
     virtual ~SensorList();
 private:
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index 5411515..a34a65b 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -17,10 +17,14 @@
 #ifndef ANDROID_SENSOR_REGISTRATION_INFO_H
 #define ANDROID_SENSOR_REGISTRATION_INFO_H
 
-#include "SensorServiceUtils.h"
-#include <utils/Thread.h>
+#include <ctime>
 #include <iomanip>
 #include <sstream>
+#include <utils/Thread.h>
+
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
+#include "SensorServiceUtils.h"
 
 namespace android {
 
@@ -30,7 +34,7 @@
 public:
     SensorRegistrationInfo() : mPackageName() {
         mSensorHandle = mSamplingRateUs = mMaxReportLatencyUs = INT32_MIN;
-        mHour = mMin = mSec = INT8_MIN;
+        mRealtimeSec = 0;
         mActivated = false;
     }
 
@@ -47,25 +51,26 @@
         mPid = (thread != nullptr) ? thread->getCallingPid() : -1;
         mUid = (thread != nullptr) ? thread->getCallingUid() : -1;
 
-        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);
-        mSec = static_cast<int8_t>(timeinfo->tm_sec);
+        timespec curTime;
+        clock_gettime(CLOCK_REALTIME_COARSE, &curTime);
+        mRealtimeSec = curTime.tv_sec;
     }
 
     static bool isSentinel(const SensorRegistrationInfo& info) {
-       return (info.mHour == INT8_MIN &&
-               info.mMin == INT8_MIN &&
-               info.mSec == INT8_MIN);
+       return (info.mSensorHandle == INT32_MIN && info.mRealtimeSec == 0);
     }
 
     // Dumpable interface
     virtual std::string dump() const override {
+        struct tm* timeinfo = localtime(&mRealtimeSec);
+        const int8_t hour = static_cast<int8_t>(timeinfo->tm_hour);
+        const int8_t min = static_cast<int8_t>(timeinfo->tm_min);
+        const int8_t sec = static_cast<int8_t>(timeinfo->tm_sec);
+
         std::ostringstream ss;
-        ss << std::setfill('0') << std::setw(2) << static_cast<int>(mHour) << ":"
-           << std::setw(2) << static_cast<int>(mMin) << ":"
-           << std::setw(2) << static_cast<int>(mSec)
+        ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":"
+           << std::setw(2) << static_cast<int>(min) << ":"
+           << std::setw(2) << static_cast<int>(sec)
            << (mActivated ? " +" : " -")
            << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec
            << std::setfill(' ') << " pid=" << std::setw(5) << mPid
@@ -77,6 +82,25 @@
         return ss.str();
     }
 
+    /**
+     * Dump debugging information as android.service.SensorRegistrationInfoProto protobuf message
+     * using ProtoOutputStream.
+     *
+     * See proto definition and some notes about ProtoOutputStream in
+     * frameworks/base/core/proto/android/service/sensor_service.proto
+     */
+    virtual void dump(util::ProtoOutputStream* proto) const override {
+        using namespace service::SensorRegistrationInfoProto;
+        proto->write(TIMESTAMP_SEC, int64_t(mRealtimeSec));
+        proto->write(SENSOR_HANDLE, mSensorHandle);
+        proto->write(PACKAGE_NAME, std::string(mPackageName.string()));
+        proto->write(PID, int32_t(mPid));
+        proto->write(UID, int32_t(mUid));
+        proto->write(SAMPLING_RATE_US, mSamplingRateUs);
+        proto->write(MAX_REPORT_LATENCY_US, mMaxReportLatencyUs);
+        proto->write(ACTIVATED, mActivated);
+    }
+
 private:
     int32_t mSensorHandle;
     String8 mPackageName;
@@ -85,8 +109,7 @@
     int64_t mSamplingRateUs;
     int64_t mMaxReportLatencyUs;
     bool mActivated;
-    int8_t mHour, mMin, mSec;
-
+    time_t mRealtimeSec;
 };
 
 } // namespace android;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 090a0ce..22a15c6 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 #include <android/content/pm/IPackageManagerNative.h>
+#include <android/util/ProtoOutputStream.h>
+#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
 #include <binder/ActivityManager.h>
 #include <binder/BinderService.h>
 #include <binder/IServiceManager.h>
@@ -403,6 +405,8 @@
                 // Transition to data injection mode supported only from NORMAL mode.
                 return INVALID_OPERATION;
             }
+        } else if (args.size() == 1 && args[0] == String16("--proto")) {
+            return dumpProtoLocked(fd, &connLock);
         } else if (!mSensors.hasAnySensor()) {
             result.append("No Sensors on the device\n");
             result.appendFormat("devInitCheck : %d\n", SensorDevice::getInstance().initCheck());
@@ -505,6 +509,128 @@
     return NO_ERROR;
 }
 
+/**
+ * Dump debugging information as android.service.SensorServiceProto protobuf message using
+ * ProtoOutputStream.
+ *
+ * See proto definition and some notes about ProtoOutputStream in
+ * frameworks/base/core/proto/android/service/sensor_service.proto
+ */
+status_t SensorService::dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const {
+    using namespace service::SensorServiceProto;
+    util::ProtoOutputStream proto;
+    proto.write(INIT_STATUS, int(SensorDevice::getInstance().initCheck()));
+    if (!mSensors.hasAnySensor()) {
+        return proto.flush(fd) ? OK : UNKNOWN_ERROR;
+    }
+    const bool privileged = IPCThreadState::self()->getCallingUid() == 0;
+
+    timespec curTime;
+    clock_gettime(CLOCK_REALTIME, &curTime);
+    proto.write(CURRENT_TIME_MS, curTime.tv_sec * 1000 + ns2ms(curTime.tv_nsec));
+
+    // Write SensorDeviceProto
+    uint64_t token = proto.start(SENSOR_DEVICE);
+    SensorDevice::getInstance().dump(&proto);
+    proto.end(token);
+
+    // Write SensorListProto
+    token = proto.start(SENSORS);
+    mSensors.dump(&proto);
+    proto.end(token);
+
+    // Write SensorFusionProto
+    token = proto.start(FUSION_STATE);
+    SensorFusion::getInstance().dump(&proto);
+    proto.end(token);
+
+    // Write SensorEventsProto
+    token = proto.start(SENSOR_EVENTS);
+    for (auto&& i : mRecentEvent) {
+        sp<SensorInterface> s = mSensors.getInterface(i.first);
+        if (!i.second->isEmpty()) {
+            i.second->setFormat(privileged || s->getSensor().getRequiredPermission().isEmpty() ?
+                    "normal" : "mask_data");
+            const uint64_t mToken = proto.start(service::SensorEventsProto::RECENT_EVENTS_LOGS);
+            proto.write(service::SensorEventsProto::RecentEventsLog::NAME,
+                    std::string(s->getSensor().getName().string()));
+            i.second->dump(&proto);
+            proto.end(mToken);
+        }
+    }
+    proto.end(token);
+
+    // Write ActiveSensorProto
+    SensorDevice& dev = SensorDevice::getInstance();
+    for (size_t i=0 ; i<mActiveSensors.size() ; i++) {
+        int handle = mActiveSensors.keyAt(i);
+        if (dev.isSensorActive(handle)) {
+            token = proto.start(ACTIVE_SENSORS);
+            proto.write(service::ActiveSensorProto::NAME,
+                    std::string(getSensorName(handle).string()));
+            proto.write(service::ActiveSensorProto::HANDLE, handle);
+            proto.write(service::ActiveSensorProto::NUM_CONNECTIONS,
+                    int(mActiveSensors.valueAt(i)->getNumConnections()));
+            proto.end(token);
+        }
+    }
+
+    proto.write(SOCKET_BUFFER_SIZE, int(mSocketBufferSize));
+    proto.write(SOCKET_BUFFER_SIZE_IN_EVENTS, int(mSocketBufferSize / sizeof(sensors_event_t)));
+    proto.write(WAKE_LOCK_ACQUIRED, mWakeLockAcquired);
+
+    switch(mCurrentOperatingMode) {
+        case NORMAL:
+            proto.write(OPERATING_MODE, OP_MODE_NORMAL);
+            break;
+        case RESTRICTED:
+            proto.write(OPERATING_MODE, OP_MODE_RESTRICTED);
+            proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string()));
+            break;
+        case DATA_INJECTION:
+            proto.write(OPERATING_MODE, OP_MODE_DATA_INJECTION);
+            proto.write(WHITELISTED_PACKAGE, std::string(mWhiteListedPackage.string()));
+            break;
+        default:
+            proto.write(OPERATING_MODE, OP_MODE_UNKNOWN);
+    }
+    proto.write(SENSOR_PRIVACY, mSensorPrivacyPolicy->isSensorPrivacyEnabled());
+
+    // Write repeated SensorEventConnectionProto
+    const auto& activeConnections = connLock->getActiveConnections();
+    for (size_t i = 0; i < activeConnections.size(); i++) {
+        token = proto.start(ACTIVE_CONNECTIONS);
+        activeConnections[i]->dump(&proto);
+        proto.end(token);
+    }
+
+    // Write repeated SensorDirectConnectionProto
+    const auto& directConnections = connLock->getDirectConnections();
+    for (size_t i = 0 ; i < directConnections.size() ; i++) {
+        token = proto.start(DIRECT_CONNECTIONS);
+        directConnections[i]->dump(&proto);
+        proto.end(token);
+    }
+
+    // Write repeated SensorRegistrationInfoProto
+    const int startIndex = mNextSensorRegIndex;
+    int curr = startIndex;
+    do {
+        const SensorRegistrationInfo& reg_info = mLastNSensorRegistrations[curr];
+        if (SensorRegistrationInfo::isSentinel(reg_info)) {
+            // Ignore sentinel, proceed to next item.
+            curr = (curr + 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE;
+            continue;
+        }
+        token = proto.start(PREVIOUS_REGISTRATIONS);
+        reg_info.dump(&proto);
+        proto.end(token);
+        curr = (curr + 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE;
+    } while (startIndex != curr);
+
+    return proto.flush(fd) ? OK : UNKNOWN_ERROR;
+}
+
 void SensorService::disableAllSensors() {
     ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
     disableAllSensorsLocked(&connLock);
@@ -1172,6 +1298,11 @@
                 return nullptr;
             }
             int fd = resource->data[0];
+            if (!ashmem_valid(fd)) {
+                ALOGE("Supplied Ashmem memory region is invalid");
+                return nullptr;
+            }
+
             int size2 = ashmem_get_size_region(fd);
             // check size consistency
             if (size2 < static_cast<int64_t>(size)) {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 060b5eb..7d17dda 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -180,7 +180,7 @@
             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) {}
+                                   int64_t procStateSeq __unused, int32_t capability __unused) {}
 
             void addOverrideUid(uid_t uid, bool active);
             void removeOverrideUid(uid_t uid);
@@ -286,6 +286,7 @@
     virtual int setOperationParameter(
             int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints);
     virtual status_t dump(int fd, const Vector<String16>& args);
+    status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const;
     String8 getSensorName(int handle) const;
     bool isVirtualSensor(int handle) const;
     sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const;
diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp
index 34cd8dd..fdd56b3 100644
--- a/services/sensorservice/SensorServiceUtils.cpp
+++ b/services/sensorservice/SensorServiceUtils.cpp
@@ -55,6 +55,7 @@
         case SENSOR_TYPE_MOTION_DETECT:
         case SENSOR_TYPE_HEART_BEAT:
         case SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT:
+        case SENSOR_TYPE_HINGE_ANGLE:
             return 1;
 
         default:
diff --git a/services/sensorservice/SensorServiceUtils.h b/services/sensorservice/SensorServiceUtils.h
index 1558feb..49457cf 100644
--- a/services/sensorservice/SensorServiceUtils.h
+++ b/services/sensorservice/SensorServiceUtils.h
@@ -21,11 +21,17 @@
 #include <string>
 
 namespace android {
+
+namespace util {
+class ProtoOutputStream;
+}
+
 namespace SensorServiceUtil {
 
 class Dumpable {
 public:
     virtual std::string dump() const = 0;
+    virtual void dump(util::ProtoOutputStream*) const {}
     virtual void setFormat(std::string ) {}
     virtual ~Dumpable() {}
 };
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 1cb0489..caf7f03 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -15,19 +15,20 @@
  */
 
 #include <inttypes.h>
+#include <android/hardware_buffer.h>
 #include <android/sensor.h>
 #include <sensor/Sensor.h>
 #include <sensor/SensorManager.h>
 #include <sensor/SensorEventQueue.h>
 #include <utils/Looper.h>
+#include <vndk/hardware_buffer.h>
 
 using namespace android;
 
 static nsecs_t sStartTime = 0;
 
 
-int receiver(__unused int fd, __unused int events, void* data)
-{
+int receiver(__unused int fd, __unused int events, void* data) {
     sp<SensorEventQueue> q((SensorEventQueue*)data);
     ssize_t n;
     ASensorEvent buffer[8];
@@ -59,11 +60,42 @@
     return 1;
 }
 
+void testInvalidSharedMem_NoCrash(SensorManager &mgr) {
+    AHardwareBuffer *hardwareBuffer;
+    char* buffer;
 
-int main()
-{
+    constexpr size_t kEventSize = sizeof(ASensorEvent);
+    constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
+    constexpr size_t kMemSize = kEventSize * kNEvent;
+    AHardwareBuffer_Desc desc = {
+            .width = static_cast<uint32_t>(kMemSize),
+            .height = 1,
+            .layers = 1,
+            .format = AHARDWAREBUFFER_FORMAT_BLOB,
+            .usage = AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA
+                        | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+    };
+
+    AHardwareBuffer_allocate(&desc, &hardwareBuffer);
+    AHardwareBuffer_lock(hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+                         -1, nullptr, reinterpret_cast<void **>(&buffer));
+
+    const native_handle_t *resourceHandle = AHardwareBuffer_getNativeHandle(hardwareBuffer);
+
+    // Pass in AHardwareBuffer, but with the wrong DIRECT_CHANNEL_TYPE to see
+    // if anything in the Sensor framework crashes
+    int ret = mgr.createDirectChannel(
+            kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, resourceHandle);
+
+    // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
+    printf("createInvalidDirectChannel=%d\n", ret);
+}
+
+int main() {
     SensorManager& mgr = SensorManager::getInstanceForPackage(String16("Sensor Service Test"));
 
+    testInvalidSharedMem_NoCrash(mgr);
+
     Sensor const* const* list;
     ssize_t count = mgr.getSensorList(&list);
     printf("numSensors=%d\n", int(count));
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
index b775431..80c3b65 100644
--- a/services/stats/StatsHal.cpp
+++ b/services/stats/StatsHal.cpp
@@ -112,9 +112,39 @@
 }
 
 hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
-  ALOGW("reportVendorAtom unsupported");
-  std::string reverDomainName = vendorAtom.reverseDomainName;
-  return hardware::Void();
+    std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
+    if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
+        ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
+        return hardware::Void();
+    }
+    if (reverseDomainName.length() > 50) {
+        ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
+        return hardware::Void();
+    }
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, vendorAtom.atomId);
+    AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str());
+    for (int i = 0; i < (int)vendorAtom.values.size(); i++) {
+        switch (vendorAtom.values[i].getDiscriminator()) {
+            case VendorAtom::Value::hidl_discriminator::intValue:
+                AStatsEvent_writeInt32(event, vendorAtom.values[i].intValue());
+                break;
+            case VendorAtom::Value::hidl_discriminator::longValue:
+                AStatsEvent_writeInt64(event, vendorAtom.values[i].longValue());
+                break;
+            case VendorAtom::Value::hidl_discriminator::floatValue:
+                AStatsEvent_writeFloat(event, vendorAtom.values[i].floatValue());
+                break;
+            case VendorAtom::Value::hidl_discriminator::stringValue:
+                AStatsEvent_writeString(event, vendorAtom.values[i].stringValue().c_str());
+                break;
+        }
+    }
+    AStatsEvent_build(event);
+    AStatsEvent_write(event);
+    AStatsEvent_release(event);
+
+    return hardware::Void();
 }
 
 }  // namespace implementation
diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h
index ad14263..071e54f 100644
--- a/services/stats/include/stats/StatsHal.h
+++ b/services/stats/include/stats/StatsHal.h
@@ -17,6 +17,8 @@
 #include <android/frameworks/stats/1.0/IStats.h>
 #include <android/frameworks/stats/1.0/types.h>
 
+#include <stats_event.h>
+
 using namespace android::frameworks::stats::V1_0;
 
 namespace android {
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index cda982a..4ffdf97 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -7,6 +7,7 @@
         "-Wthread-safety",
         "-Wunused",
         "-Wunreachable-code",
+        "-Wconversion",
     ],
 }
 
@@ -19,7 +20,7 @@
         "-DEGL_EGLEXT_PROTOTYPES",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "android.hardware.configstore-utils",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore@1.1",
@@ -29,6 +30,7 @@
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
         "libbase",
@@ -50,14 +52,23 @@
         "libprocessgroup",
         "libprotobuf-cpp-lite",
         "libsync",
-        "libtimestats_proto",
+        "libtimestats",
         "libui",
         "libinput",
         "libutils",
         "libSurfaceFlingerProp",
     ],
+    // VrComposer is not used when building surfaceflinger for vendors
+    target: {
+        vendor: {
+            exclude_shared_libs: [
+                "android.frameworks.vr.composer@2.0",
+            ],
+        },
+    },
     static_libs: [
         "libcompositionengine",
+        "libperfetto_client_experimental",
         "librenderengine",
         "libserviceutils",
         "libtrace_proto",
@@ -68,6 +79,7 @@
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
+        "android.hardware.graphics.composer@2.4-command-buffer",
     ],
     export_static_lib_headers: [
         "libcompositionengine",
@@ -81,9 +93,15 @@
         "android.hardware.graphics.composer@2.1",
         "android.hardware.graphics.composer@2.2",
         "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
         "android.hardware.power@1.3",
         "libhidlbase",
+        "libtimestats",
     ],
+    // TODO (marissaw): this library is not used by surfaceflinger. This is here so
+    // the library compiled in a way that is accessible to system partition when running
+    // IMapper's VTS.
+    required: ["libgralloctypes"]
 }
 
 cc_defaults {
@@ -118,7 +136,7 @@
         "BufferStateLayer.cpp",
         "ClientCache.cpp",
         "Client.cpp",
-        "ColorLayer.cpp",
+        "EffectLayer.cpp",
         "ContainerLayer.cpp",
         "DisplayDevice.cpp",
         "DisplayHardware/ComposerHal.cpp",
@@ -130,11 +148,11 @@
         "DisplayHardware/VirtualDisplaySurface.cpp",
         "Effects/Daltonizer.cpp",
         "EventLog/EventLog.cpp",
+        "FrameTracer/FrameTracer.cpp",
         "FrameTracker.cpp",
         "Layer.cpp",
         "LayerProtoHelper.cpp",
         "LayerRejecter.cpp",
-        "LayerStats.cpp",
         "LayerVector.cpp",
         "MonitoredProducer.cpp",
         "NativeWindowSurface.cpp",
@@ -145,19 +163,26 @@
         "Scheduler/DispSyncSource.cpp",
         "Scheduler/EventControlThread.cpp",
         "Scheduler/EventThread.cpp",
-        "Scheduler/IdleTimer.cpp",
+        "Scheduler/OneShotTimer.cpp",
         "Scheduler/LayerHistory.cpp",
+        "Scheduler/LayerHistoryV2.cpp",
         "Scheduler/LayerInfo.cpp",
+        "Scheduler/LayerInfoV2.cpp",
         "Scheduler/MessageQueue.cpp",
         "Scheduler/PhaseOffsets.cpp",
+        "Scheduler/RefreshRateConfigs.cpp",
         "Scheduler/Scheduler.cpp",
         "Scheduler/SchedulerUtils.cpp",
+        "Scheduler/Timer.cpp",
+        "Scheduler/VSyncDispatchTimerQueue.cpp",
+        "Scheduler/VSyncPredictor.cpp",
         "Scheduler/VSyncModulator.cpp",
+        "Scheduler/VSyncReactor.cpp",
         "StartPropertySetThread.cpp",
         "SurfaceFlinger.cpp",
+        "SurfaceFlingerDefaultFactory.cpp",
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
-        "TimeStats/TimeStats.cpp",
         "TransactionCompletedThread.cpp",
     ],
 }
@@ -174,6 +199,17 @@
         // can be easily replaced.
         "SurfaceFlingerFactory.cpp",
     ],
+    cflags: [
+        "-DUSE_VR_COMPOSER=1",
+    ],
+    // VrComposer is not used when building surfaceflinger for vendors
+    target: {
+        vendor: {
+            cflags: [
+                "-DUSE_VR_COMPOSER=0",
+            ],
+        },
+    },
     logtags: ["EventLog/EventLogTags.logtags"],
 }
 
@@ -198,7 +234,6 @@
         "liblog",
         "libprocessgroup",
         "libsync",
-        "libtimestats_proto",
         "libutils",
     ],
     static_libs: [
@@ -226,7 +261,6 @@
 
 subdirs = [
     "layerproto",
-    "TimeStats/timestatsproto",
     "tests",
 ]
 
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f51fbb4..d7ec868 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "BufferLayer"
@@ -22,17 +26,15 @@
 #include "BufferLayer.h"
 
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
 #include <cutils/properties.h>
 #include <gui/BufferItem.h>
 #include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/Surface.h>
 #include <renderengine/RenderEngine.h>
@@ -50,17 +52,20 @@
 
 #include "Colorizer.h"
 #include "DisplayDevice.h"
+#include "FrameTracer/FrameTracer.h"
 #include "LayerRejecter.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
 
+static constexpr float defaultMaxMasteringLuminance = 1000.0;
+static constexpr float defaultMaxContentLuminance = 1000.0;
+
 BufferLayer::BufferLayer(const LayerCreationArgs& args)
       : Layer(args),
-        mTextureName(args.flinger->getNewTexture()),
-        mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
-                compositionengine::LayerCreationArgs{this})} {
-    ALOGV("Creating Layer %s", args.name.string());
+        mTextureName(args.textureName),
+        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {
+    ALOGV("Creating Layer %s", getDebugName());
 
     mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
 
@@ -69,15 +74,23 @@
 }
 
 BufferLayer::~BufferLayer() {
-    mFlinger->deleteTextureAsync(mTextureName);
-    mFlinger->mTimeStats->onDestroy(getSequence());
+    if (!isClone()) {
+        // The original layer and the clone layer share the same texture. Therefore, only one of
+        // the layers, in this case the original layer, needs to handle the deletion. The original
+        // layer and the clone should be removed at the same time so there shouldn't be any issue
+        // with the clone layer trying to use the deleted texture.
+        mFlinger->deleteTextureAsync(mTextureName);
+    }
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
 void BufferLayer::useSurfaceDamage() {
     if (mFlinger->mForceFullDamage) {
         surfaceDamageRegion = Region::INVALID_REGION;
     } else {
-        surfaceDamageRegion = getDrawingSurfaceDamage();
+        surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
     }
 }
 
@@ -88,7 +101,7 @@
 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 ((mSidebandStream == nullptr) && (mActiveBuffer == nullptr)) {
+    if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) {
         return false;
     }
 
@@ -98,11 +111,8 @@
 }
 
 bool BufferLayer::isVisible() const {
-    bool visible = !(isHiddenByPolicy()) && getAlpha() > 0.0f &&
-            (mActiveBuffer != nullptr || mSidebandStream != nullptr);
-    mFlinger->mScheduler->setLayerVisibility(mSchedulerLayerHandle, visible);
-
-    return visible;
+    return !isHiddenByPolicy() && getAlpha() > 0.0f &&
+            (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr);
 }
 
 bool BufferLayer::isFixedSize() const {
@@ -131,14 +141,17 @@
     return inverse(tr);
 }
 
-bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                     bool useIdentityTransform, Region& clearRegion,
-                                     const bool supportProtectedContent,
-                                     renderengine::LayerSettings& layer) {
+std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareClientComposition(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
     ATRACE_CALL();
-    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion,
-                              supportProtectedContent, layer);
-    if (CC_UNLIKELY(mActiveBuffer == 0)) {
+
+    std::optional<compositionengine::LayerFE::LayerSettings> result =
+            Layer::prepareClientComposition(targetSettings);
+    if (!result) {
+        return result;
+    }
+
+    if (CC_UNLIKELY(mBufferInfo.mBuffer == 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
@@ -155,192 +168,143 @@
                 finished = true;
                 return;
             }
-            under.orSelf(layer->visibleRegion);
+
+            under.orSelf(layer->getScreenBounds());
         });
         // if not everything below us is covered, we plug the holes!
-        Region holes(clip.subtract(under));
+        Region holes(targetSettings.clip.subtract(under));
         if (!holes.isEmpty()) {
-            clearRegion.orSelf(holes);
+            targetSettings.clearRegion.orSelf(holes);
         }
-        return false;
+        return std::nullopt;
     }
-    bool blackOutLayer =
-            (isProtected() && !supportProtectedContent) || (isSecure() && !renderArea.isSecure());
+    bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) ||
+            (isSecure() && !targetSettings.isSecure);
+    compositionengine::LayerFE::LayerSettings& layer = *result;
+    if (blackOutLayer) {
+        prepareClearClientComposition(layer, true /* blackout */);
+        return layer;
+    }
+
     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.textureName = mTextureName;
-        layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
-        layer.source.buffer.isY410BT2020 = isHdrY410();
-        // TODO: we could be more subtle with isFixedSize()
-        const bool useFiltering = needsFiltering(renderArea.getDisplayDevice()) ||
-                renderArea.needsFiltering() || isFixedSize();
+    layer.source.buffer.buffer = mBufferInfo.mBuffer;
+    layer.source.buffer.isOpaque = isOpaque(s);
+    layer.source.buffer.fence = mBufferInfo.mFence;
+    layer.source.buffer.textureName = mTextureName;
+    layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
+    layer.source.buffer.isY410BT2020 = isHdrY410();
+    bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+    bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+    layer.source.buffer.maxMasteringLuminance = hasSmpte2086
+            ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
+            : defaultMaxMasteringLuminance;
+    layer.source.buffer.maxContentLuminance = hasCta861_3
+            ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
+            : defaultMaxContentLuminance;
+    layer.frameNumber = mCurrentFrameNumber;
+    layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0;
 
-        // Query the texture matrix given our current filtering mode.
-        float textureMatrix[16];
-        setFilteringEnabled(useFiltering);
-        getDrawingTransformMatrix(textureMatrix);
+    // TODO: we could be more subtle with isFixedSize()
+    const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
 
-        if (getTransformToDisplayInverse()) {
-            /*
-             * the code below applies the primary display's inverse transform to
-             * the texture transform
-             */
-            uint32_t transform = DisplayDevice::getPrimaryDisplayOrientationTransform();
-            mat4 tr = inverseOrientation(transform);
+    // Query the texture matrix given our current filtering mode.
+    float textureMatrix[16];
+    getDrawingTransformMatrix(useFiltering, textureMatrix);
 
-            /**
-             * TODO(b/36727915): This is basically a hack.
-             *
-             * Ensure that regardless of the parent transformation,
-             * this buffer is always transformed from native display
-             * orientation to display orientation. For example, in the case
-             * of a camera where the buffer remains in native orientation,
-             * we want the pixels to always be upright.
-             */
-            sp<Layer> p = mDrawingParent.promote();
-            if (p != nullptr) {
-                const auto parentTransform = p->getTransform();
-                tr = tr * inverseOrientation(parentTransform.getOrientation());
-            }
+    if (getTransformToDisplayInverse()) {
+        /*
+         * the code below applies the primary display's inverse transform to
+         * the texture transform
+         */
+        uint32_t transform = DisplayDevice::getPrimaryDisplayRotationFlags();
+        mat4 tr = inverseOrientation(transform);
 
-            // and finally apply it to the original texture matrix
-            const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
-            memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
+        /**
+         * TODO(b/36727915): This is basically a hack.
+         *
+         * Ensure that regardless of the parent transformation,
+         * this buffer is always transformed from native display
+         * orientation to display orientation. For example, in the case
+         * of a camera where the buffer remains in native orientation,
+         * we want the pixels to always be upright.
+         */
+        sp<Layer> p = mDrawingParent.promote();
+        if (p != nullptr) {
+            const auto parentTransform = p->getTransform();
+            tr = tr * inverseOrientation(parentTransform.getOrientation());
         }
 
-        const Rect win{getBounds()};
-        float bufferWidth = getBufferSize(s).getWidth();
-        float bufferHeight = getBufferSize(s).getHeight();
-
-        // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
-        // been set and there is no parent layer bounds. In that case, the scale is meaningless so
-        // ignore them.
-        if (!getBufferSize(s).isValid()) {
-            bufferWidth = float(win.right) - float(win.left);
-            bufferHeight = float(win.bottom) - float(win.top);
-        }
-
-        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 {
-        // 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;
+        // and finally apply it to the original texture matrix
+        const mat4 texTransform(mat4(static_cast<const float*>(textureMatrix)) * tr);
+        memcpy(textureMatrix, texTransform.asArray(), sizeof(textureMatrix));
     }
 
-    return true;
+    const Rect win{getBounds()};
+    float bufferWidth = getBufferSize(s).getWidth();
+    float bufferHeight = getBufferSize(s).getHeight();
+
+    // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has
+    // been set and there is no parent layer bounds. In that case, the scale is meaningless so
+    // ignore them.
+    if (!getBufferSize(s).isValid()) {
+        bufferWidth = float(win.right) - float(win.left);
+        bufferHeight = float(win.bottom) - float(win.top);
+    }
+
+    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;
+
+    return layer;
 }
 
 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 &&
-            mActiveBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
+    return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ &&
+            mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA &&
+            mBufferInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102);
 }
 
-void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice,
-                                  const ui::Transform& transform, const Rect& viewport,
-                                  int32_t supportedPerFrameMetadata,
-                                  const ui::Dataspace targetDataspace) {
-    RETURN_IF_NO_HWC_LAYER(displayDevice);
+sp<compositionengine::LayerFE> BufferLayer::getCompositionEngineLayerFE() const {
+    return asLayerFE();
+}
 
-    // 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));
+compositionengine::LayerFECompositionState* BufferLayer::editCompositionState() {
+    return mCompositionState.get();
+}
 
-    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
+const compositionengine::LayerFECompositionState* BufferLayer::getCompositionState() const {
+    return mCompositionState.get();
+}
 
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-    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);
-    }
-    outputLayer->editState().visibleRegion = visible;
-
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-
-    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);
-    }
-    layerCompositionState.surfaceDamage = surfaceDamageRegion;
+void BufferLayer::preparePerFrameCompositionState() {
+    Layer::preparePerFrameCompositionState();
 
     // Sideband layers
-    if (layerCompositionState.sidebandStream.get()) {
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::SIDEBAND);
-        ALOGV("[%s] Requesting Sideband composition", mName.string());
-        error = hwcLayer->setSidebandStream(layerCompositionState.sidebandStream->handle());
-        if (error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(),
-                  layerCompositionState.sidebandStream->handle(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-        layerCompositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
-        return;
-    }
-
-    // Device or Cursor layers
-    if (mPotentialCursor) {
-        ALOGV("[%s] Requesting Cursor composition", mName.string());
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CURSOR);
+    auto* compositionState = editCompositionState();
+    if (compositionState->sidebandStream.get()) {
+        compositionState->compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
     } else {
-        ALOGV("[%s] Requesting Device composition", mName.string());
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE);
+        // Normal buffer layers
+        compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
+        compositionState->compositionType = mPotentialCursor
+                ? Hwc2::IComposerClient::Composition::CURSOR
+                : Hwc2::IComposerClient::Composition::DEVICE;
     }
-
-    ui::Dataspace dataspace = isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN
-            ? targetDataspace
-            : mCurrentDataSpace;
-    error = hwcLayer->setDataspace(dataspace);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
-              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::Unsupported) {
-        // If per layer color transform is not supported, we use GPU composition.
-        setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CLIENT);
-    } else if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(),
-                to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-    layerCompositionState.dataspace = mCurrentDataSpace;
-    layerCompositionState.colorTransform = getColorTransform();
-    layerCompositionState.hdrMetadata = metadata;
-
-    setHwcLayerBuffer(displayDevice);
 }
 
 bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) {
-    if (mBufferLatched) {
+    if (mBufferInfo.mBuffer != nullptr) {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
         mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime);
     }
@@ -348,29 +312,38 @@
     return hasReadyFrame();
 }
 
-bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId,
+bool BufferLayer::onPostComposition(sp<const DisplayDevice> displayDevice,
                                     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
     // composition.
-    if (!mFrameLatencyNeeded) return false;
+    if (!mBufferInfo.mFrameLatencyNeeded) return false;
 
     // Update mFrameEventHistory.
     {
         Mutex::Autolock lock(mFrameEventHistoryMutex);
         mFrameEventHistory.addPostComposition(mCurrentFrameNumber, glDoneFence, presentFence,
                                               compositorTiming);
+        finalizeFrameEventHistory(glDoneFence, compositorTiming);
     }
 
     // Update mFrameTracker.
-    nsecs_t desiredPresentTime = getDesiredPresentTime();
+    nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime;
     mFrameTracker.setDesiredPresentTime(desiredPresentTime);
 
-    const int32_t layerID = getSequence();
-    mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime);
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime);
 
-    std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime();
+    const auto outputLayer = findOutputLayerForDisplay(displayDevice);
+    if (outputLayer && outputLayer->requiresClientComposition()) {
+        nsecs_t clientCompositionTimestamp = outputLayer->getState().clientCompositionTimestamp;
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               clientCompositionTimestamp,
+                                               FrameTracer::FrameEvent::FALLBACK_COMPOSITION);
+    }
+
+    std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime;
     if (frameReadyFence->isValid()) {
         mFrameTracker.setFrameReadyFence(std::move(frameReadyFence));
     } else {
@@ -379,23 +352,30 @@
         mFrameTracker.setFrameReadyTime(desiredPresentTime);
     }
 
+    const auto displayId = displayDevice->getId();
     if (presentFence->isValid()) {
-        mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence);
+        mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence);
+        mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                           presentFence, FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence));
     } 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(*displayId);
-        mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime);
+        mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber,
+                                               actualPresentTime,
+                                               FrameTracer::FrameEvent::PRESENT_FENCE);
         mFrameTracker.setActualPresentTime(actualPresentTime);
     }
 
     mFrameTracker.advanceFrame();
-    mFrameLatencyNeeded = false;
+    mBufferInfo.mFrameLatencyNeeded = false;
     return true;
 }
 
-bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
+bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                              nsecs_t expectedPresentTime) {
     ATRACE_CALL();
 
     bool refreshRequired = latchSidebandStream(recomputeVisibleRegions);
@@ -428,14 +408,15 @@
     // Capture the old state of the layer for comparisons later
     const State& s(getDrawingState());
     const bool oldOpacity = isOpaque(s);
-    sp<GraphicBuffer> oldBuffer = mActiveBuffer;
 
-    if (!allTransactionsSignaled()) {
+    BufferInfo oldBufferInfo = mBufferInfo;
+
+    if (!allTransactionsSignaled(expectedPresentTime)) {
         mFlinger->setTransactionFlags(eTraversalNeeded);
         return false;
     }
 
-    status_t err = updateTexImage(recomputeVisibleRegions, latchTime);
+    status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
     if (err != NO_ERROR) {
         return false;
     }
@@ -445,65 +426,33 @@
         return false;
     }
 
-    mBufferLatched = true;
-
     err = updateFrameNumber(latchTime);
     if (err != NO_ERROR) {
         return false;
     }
 
+    gatherBufferInfo();
+
     mRefreshPending = true;
-    mFrameLatencyNeeded = true;
-    if (oldBuffer == nullptr) {
+    mBufferInfo.mFrameLatencyNeeded = true;
+    if (oldBufferInfo.mBuffer == nullptr) {
         // the first time we receive a buffer, we need to trigger a
         // geometry invalidation.
         recomputeVisibleRegions = true;
     }
 
-    ui::Dataspace dataSpace = getDrawingDataSpace();
-    // translate legacy dataspaces to modern dataspaces
-    switch (dataSpace) {
-        case ui::Dataspace::SRGB:
-            dataSpace = ui::Dataspace::V0_SRGB;
-            break;
-        case ui::Dataspace::SRGB_LINEAR:
-            dataSpace = ui::Dataspace::V0_SRGB_LINEAR;
-            break;
-        case ui::Dataspace::JFIF:
-            dataSpace = ui::Dataspace::V0_JFIF;
-            break;
-        case ui::Dataspace::BT601_625:
-            dataSpace = ui::Dataspace::V0_BT601_625;
-            break;
-        case ui::Dataspace::BT601_525:
-            dataSpace = ui::Dataspace::V0_BT601_525;
-            break;
-        case ui::Dataspace::BT709:
-            dataSpace = ui::Dataspace::V0_BT709;
-            break;
-        default:
-            break;
-    }
-    mCurrentDataSpace = dataSpace;
-
-    Rect crop(getDrawingCrop());
-    const uint32_t transform(getDrawingTransform());
-    const uint32_t scalingMode(getDrawingScalingMode());
-    const bool transformToDisplayInverse(getTransformToDisplayInverse());
-    if ((crop != mCurrentCrop) || (transform != mCurrentTransform) ||
-        (scalingMode != mCurrentScalingMode) ||
-        (transformToDisplayInverse != mTransformToDisplayInverse)) {
-        mCurrentCrop = crop;
-        mCurrentTransform = transform;
-        mCurrentScalingMode = scalingMode;
-        mTransformToDisplayInverse = transformToDisplayInverse;
+    if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
+        (mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
+        (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
+        (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
         recomputeVisibleRegions = true;
     }
 
-    if (oldBuffer != nullptr) {
-        uint32_t bufWidth = mActiveBuffer->getWidth();
-        uint32_t bufHeight = mActiveBuffer->getHeight();
-        if (bufWidth != uint32_t(oldBuffer->width) || bufHeight != uint32_t(oldBuffer->height)) {
+    if (oldBufferInfo.mBuffer != nullptr) {
+        uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+        uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
+        if (bufWidth != uint32_t(oldBufferInfo.mBuffer->width) ||
+            bufHeight != uint32_t(oldBufferInfo.mBuffer->height)) {
             recomputeVisibleRegions = true;
         }
     }
@@ -540,10 +489,10 @@
 }
 
 // transaction
-void BufferLayer::notifyAvailableFrames() {
-    const auto headFrameNumber = getHeadFrameNumber();
+void BufferLayer::notifyAvailableFrames(nsecs_t expectedPresentTime) {
+    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
     const bool headFenceSignaled = fenceHasSignaled();
-    const bool presentTimeIsCurrent = framePresentTimeIsCurrent();
+    const bool presentTimeIsCurrent = framePresentTimeIsCurrent(expectedPresentTime);
     Mutex::Autolock lock(mLocalSyncPointMutex);
     for (auto& point : mLocalSyncPoints) {
         if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled &&
@@ -568,11 +517,11 @@
         return mOverrideScalingMode;
     }
 
-    return mCurrentScalingMode;
+    return mBufferInfo.mScaleMode;
 }
 
 bool BufferLayer::isProtected() const {
-    const sp<GraphicBuffer>& buffer(mActiveBuffer);
+    const sp<GraphicBuffer>& buffer(mBufferInfo.mBuffer);
     return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
 }
 
@@ -591,8 +540,8 @@
 }
 
 // h/w composer set-up
-bool BufferLayer::allTransactionsSignaled() {
-    auto headFrameNumber = getHeadFrameNumber();
+bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) {
+    const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime);
     bool matchingFramesFound = false;
     bool allTransactionsApplied = true;
     Mutex::Autolock lock(mLocalSyncPointMutex);
@@ -640,27 +589,29 @@
 }
 
 bool BufferLayer::needsFiltering(const sp<const DisplayDevice>& displayDevice) const {
-    // If we are not capturing based on the state of a known display device, we
-    // only return mNeedsFiltering
+    // If we are not capturing based on the state of a known display device,
+    // just return false.
     if (displayDevice == nullptr) {
-        return mNeedsFiltering;
+        return false;
     }
 
     const auto outputLayer = findOutputLayerForDisplay(displayDevice);
     if (outputLayer == nullptr) {
-        return mNeedsFiltering;
+        return false;
     }
 
+    // We need filtering if the sourceCrop rectangle size does not match the
+    // displayframe rectangle size (not a 1:1 render)
     const auto& compositionState = outputLayer->getState();
     const auto displayFrame = compositionState.displayFrame;
     const auto sourceCrop = compositionState.sourceCrop;
-    return mNeedsFiltering || sourceCrop.getHeight() != displayFrame.getHeight() ||
+    return sourceCrop.getHeight() != displayFrame.getHeight() ||
             sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
-uint64_t BufferLayer::getHeadFrameNumber() const {
+uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const {
     if (hasFrameUpdate()) {
-        return getFrameNumber();
+        return getFrameNumber(expectedPresentTime);
     } else {
         return mCurrentFrameNumber;
     }
@@ -674,20 +625,20 @@
         return Rect(getActiveWidth(s), getActiveHeight(s));
     }
 
-    if (mActiveBuffer == nullptr) {
+    if (mBufferInfo.mBuffer == nullptr) {
         return Rect::INVALID_RECT;
     }
 
-    uint32_t bufWidth = mActiveBuffer->getWidth();
-    uint32_t bufHeight = mActiveBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
 
     // Undo any transformations on the buffer and return the result.
-    if (mCurrentTransform & ui::Transform::ROT_90) {
+    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
         std::swap(bufWidth, bufHeight);
     }
 
     if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufWidth, bufHeight);
         }
@@ -696,10 +647,6 @@
     return Rect(bufWidth, bufHeight);
 }
 
-std::shared_ptr<compositionengine::Layer> BufferLayer::getCompositionLayer() const {
-    return mCompositionLayer;
-}
-
 FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const {
     const State& s(getDrawingState());
 
@@ -710,20 +657,20 @@
         return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s));
     }
 
-    if (mActiveBuffer == nullptr) {
+    if (mBufferInfo.mBuffer == nullptr) {
         return parentBounds;
     }
 
-    uint32_t bufWidth = mActiveBuffer->getWidth();
-    uint32_t bufHeight = mActiveBuffer->getHeight();
+    uint32_t bufWidth = mBufferInfo.mBuffer->getWidth();
+    uint32_t bufHeight = mBufferInfo.mBuffer->getHeight();
 
     // Undo any transformations on the buffer and return the result.
-    if (mCurrentTransform & ui::Transform::ROT_90) {
+    if (mBufferInfo.mTransform & ui::Transform::ROT_90) {
         std::swap(bufWidth, bufHeight);
     }
 
     if (getTransformToDisplayInverse()) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufWidth, bufHeight);
         }
@@ -732,6 +679,125 @@
     return FloatRect(0, 0, bufWidth, bufHeight);
 }
 
+void BufferLayer::latchAndReleaseBuffer() {
+    mRefreshPending = false;
+    if (hasReadyFrame()) {
+        bool ignored = false;
+        latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */);
+    }
+    releasePendingBuffer(systemTime());
+}
+
+PixelFormat BufferLayer::getPixelFormat() const {
+    return mBufferInfo.mPixelFormat;
+}
+
+bool BufferLayer::getTransformToDisplayInverse() const {
+    return mBufferInfo.mTransformToDisplayInverse;
+}
+
+Rect BufferLayer::getBufferCrop() const {
+    // this is the crop rectangle that applies to the buffer
+    // itself (as opposed to the window)
+    if (!mBufferInfo.mCrop.isEmpty()) {
+        // if the buffer crop is defined, we use that
+        return mBufferInfo.mCrop;
+    } else if (mBufferInfo.mBuffer != nullptr) {
+        // otherwise we use the whole buffer
+        return mBufferInfo.mBuffer->getBounds();
+    } else {
+        // if we don't have a buffer yet, we use an empty/invalid crop
+        return Rect();
+    }
+}
+
+uint32_t BufferLayer::getBufferTransform() const {
+    return mBufferInfo.mTransform;
+}
+
+ui::Dataspace BufferLayer::getDataSpace() const {
+    return mBufferInfo.mDataspace;
+}
+
+ui::Dataspace BufferLayer::translateDataspace(ui::Dataspace dataspace) {
+    ui::Dataspace updatedDataspace = dataspace;
+    // translate legacy dataspaces to modern dataspaces
+    switch (dataspace) {
+        case ui::Dataspace::SRGB:
+            updatedDataspace = ui::Dataspace::V0_SRGB;
+            break;
+        case ui::Dataspace::SRGB_LINEAR:
+            updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+            break;
+        case ui::Dataspace::JFIF:
+            updatedDataspace = ui::Dataspace::V0_JFIF;
+            break;
+        case ui::Dataspace::BT601_625:
+            updatedDataspace = ui::Dataspace::V0_BT601_625;
+            break;
+        case ui::Dataspace::BT601_525:
+            updatedDataspace = ui::Dataspace::V0_BT601_525;
+            break;
+        case ui::Dataspace::BT709:
+            updatedDataspace = ui::Dataspace::V0_BT709;
+            break;
+        default:
+            break;
+    }
+
+    return updatedDataspace;
+}
+
+sp<GraphicBuffer> BufferLayer::getBuffer() const {
+    return mBufferInfo.mBuffer;
+}
+
+void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) {
+    GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop,
+                                       mBufferInfo.mTransform, filteringEnabled);
+}
+
+void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+    Layer::setInitialValuesForClone(clonedFrom);
+
+    sp<BufferLayer> bufferClonedFrom = static_cast<BufferLayer*>(clonedFrom.get());
+    mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha;
+    mPotentialCursor = bufferClonedFrom->mPotentialCursor;
+    mProtectedByApp = bufferClonedFrom->mProtectedByApp;
+
+    updateCloneBufferInfo();
+}
+
+void BufferLayer::updateCloneBufferInfo() {
+    if (!isClone() || !isClonedFromAlive()) {
+        return;
+    }
+
+    sp<BufferLayer> clonedFrom = static_cast<BufferLayer*>(getClonedFrom().get());
+    mBufferInfo = clonedFrom->mBufferInfo;
+    mSidebandStream = clonedFrom->mSidebandStream;
+    surfaceDamageRegion = clonedFrom->surfaceDamageRegion;
+    mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load();
+    mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber;
+
+    // After buffer info is updated, the drawingState from the real layer needs to be copied into
+    // the cloned. This is because some properties of drawingState can change when latchBuffer is
+    // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+    // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+    // the cloned drawingState again.
+    wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
+    SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+    wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+    InputWindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
+    mDrawingState = clonedFrom->mDrawingState;
+
+    mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
+    mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
+    mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+    mDrawingState.inputInfo = tmpInputInfo;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
@@ -741,3 +807,6 @@
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
 #endif
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 46a62ed..f678910 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -54,7 +54,8 @@
     // Overriden from Layer
     // -----------------------------------------------------------------------
 public:
-    std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
+    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
+    compositionengine::LayerFECompositionState* editCompositionState() 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
@@ -62,10 +63,6 @@
     void useSurfaceDamage() override;
     void useEmptyDamage() 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;
 
     // isVisible - true if this layer is visible, false otherwise
@@ -82,12 +79,7 @@
 
     bool isHdrY410() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
-    bool onPreComposition(nsecs_t refreshStartTime) override;
-    bool onPostComposition(const std::optional<DisplayId>& displayId,
+    bool onPostComposition(sp<const DisplayDevice> displayDevice,
                            const std::shared_ptr<FenceTime>& glDoneFence,
                            const std::shared_ptr<FenceTime>& presentFence,
                            const CompositorTiming& compositorTiming) override;
@@ -96,17 +88,35 @@
     // 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.
-    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                     nsecs_t expectedPresentTime) override;
 
     bool isBufferLatched() const override { return mRefreshPending; }
 
-    void notifyAvailableFrames() override;
+    void notifyAvailableFrames(nsecs_t expectedPresentTime) 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;
+
+    // Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
+    // This is used if the buffer is just latched and releases to free up the buffer
+    // and will not be shown on screen.
+    // Should only be called on the main thread.
+    void latchAndReleaseBuffer() override;
+
+    bool getTransformToDisplayInverse() const override;
+
+    Rect getBufferCrop() const override;
+
+    uint32_t getBufferTransform() const override;
+
+    ui::Dataspace getDataSpace() const override;
+
+    sp<GraphicBuffer> getBuffer() const override;
+
     // -----------------------------------------------------------------------
 
     // -----------------------------------------------------------------------
@@ -114,22 +124,15 @@
     // -----------------------------------------------------------------------
 private:
     virtual bool fenceHasSignaled() const = 0;
-    virtual bool framePresentTimeIsCurrent() const = 0;
+    virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0;
 
-    virtual nsecs_t getDesiredPresentTime() = 0;
-    virtual std::shared_ptr<FenceTime> getCurrentFenceTime() const = 0;
+    PixelFormat getPixelFormat() const;
 
-    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;
+    // Computes the transform matrix using the setFilteringEnabled to determine whether the
+    // transform matrix should be computed for use with bilinear filtering.
+    void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]);
 
-    virtual uint64_t getFrameNumber() const = 0;
+    virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0;
 
     virtual bool getAutoRefresh() const = 0;
     virtual bool getSidebandStreamChanged() const = 0;
@@ -139,56 +142,78 @@
 
     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) = 0;
+    virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                    nsecs_t expectedPresentTime) = 0;
 
     virtual status_t updateActiveBuffer() = 0;
     virtual status_t updateFrameNumber(nsecs_t latchTime) = 0;
 
-    virtual void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) = 0;
-
 protected:
+    struct BufferInfo {
+        nsecs_t mDesiredPresentTime;
+        std::shared_ptr<FenceTime> mFenceTime;
+        sp<Fence> mFence;
+        uint32_t mTransform{0};
+        ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
+        Rect mCrop;
+        uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
+        Region mSurfaceDamage;
+        HdrMetadata mHdrMetadata;
+        int mApi;
+        PixelFormat mPixelFormat;
+        bool mTransformToDisplayInverse{false};
+
+        sp<GraphicBuffer> mBuffer;
+        int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
+
+        bool mFrameLatencyNeeded{false};
+    };
+
+    BufferInfo mBufferInfo;
+    virtual void gatherBufferInfo() = 0;
+
+    std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+
+    /*
+     * compositionengine::LayerFE overrides
+     */
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    bool onPreComposition(nsecs_t) override;
+    void preparePerFrameCompositionState() override;
+
     // 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();
+    bool allTransactionsSignaled(nsecs_t expectedPresentTime);
 
     static bool getOpacityForFormat(uint32_t format);
 
-    // from GLES
+    // from graphics API
     const uint32_t mTextureName;
 
     bool mRefreshPending{false};
 
-    // prepareClientLayer - constructs a RenderEngine layer for GPU composition.
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                            bool useIdentityTransform, Region& clearRegion,
-                            const bool supportProtectedContent,
-                            renderengine::LayerSettings& layer) override;
+    ui::Dataspace translateDataspace(ui::Dataspace dataspace);
+    void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+    void updateCloneBufferInfo() override;
+    uint64_t mPreviousFrameNumber = 0;
+
+    virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
 private:
     // Returns true if this layer requires filtering
-    bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const;
-
-    uint64_t getHeadFrameNumber() const;
-
-    uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
-
-    bool mTransformToDisplayInverse{false};
-
-    // main thread.
-    bool mBufferLatched{false}; // TODO: Use mActiveBuffer?
+    bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const override;
 
     // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame
     // and its parent layer is not bounded
     Rect getBufferSize(const State& s) const override;
 
-    std::shared_ptr<compositionengine::Layer> mCompositionLayer;
+    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
 
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
 };
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 414814a..5e04d95 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "BufferLayerConsumer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -428,30 +432,6 @@
     return mCurrentFenceTime;
 }
 
-status_t BufferLayerConsumer::doFenceWaitLocked() const {
-    if (mCurrentFence->isValid()) {
-        if (mRE.useWaitSync()) {
-            base::unique_fd fenceFd(mCurrentFence->dup());
-            if (fenceFd == -1) {
-                BLC_LOGE("doFenceWait: error dup'ing fence fd: %d", errno);
-                return -errno;
-            }
-            if (!mRE.waitFence(std::move(fenceFd))) {
-                BLC_LOGE("doFenceWait: failed to wait on fence fd");
-                return UNKNOWN_ERROR;
-            }
-        } else {
-            status_t err = mCurrentFence->waitForever("BufferLayerConsumer::doFenceWaitLocked");
-            if (err != NO_ERROR) {
-                BLC_LOGE("doFenceWait: error waiting for fence: %d", err);
-                return err;
-            }
-        }
-    }
-
-    return NO_ERROR;
-}
-
 void BufferLayerConsumer::freeBufferLocked(int slotIndex) {
     BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
     std::lock_guard<std::mutex> lock(mImagesMutex);
@@ -543,3 +523,6 @@
     }
 }
 }; // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 617b1c2..39ed370 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -254,11 +254,6 @@
     // mCurrentTextureImage must not be nullptr.
     void computeCurrentTransformMatrixLocked();
 
-    // doFenceWaitLocked inserts a wait command into the RenderEngine command
-    // stream to ensure that it is safe for future RenderEngine commands to
-    // access the current texture buffer.
-    status_t doFenceWaitLocked() const;
-
     // getCurrentCropLocked returns the cropping rectangle of the current buffer.
     Rect getCurrentCropLocked() const;
 
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index af5fe62..e65064b 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -14,21 +14,23 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "BufferQueueLayer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include "BufferQueueLayer.h"
+
+#include <compositionengine/LayerFECompositionState.h>
 #include <gui/BufferQueueConsumer.h>
 #include <system/window.h>
 
-#include "BufferQueueLayer.h"
 #include "LayerRejecter.h"
 #include "SurfaceInterceptor.h"
 
+#include "FrameTracer/FrameTracer.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -36,6 +38,7 @@
 BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
 
 BufferQueueLayer::~BufferQueueLayer() {
+    mContentsChangedListener->abandon();
     mConsumer->abandon();
 }
 
@@ -45,6 +48,14 @@
 
 void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) {
     mConsumer->setReleaseFence(releaseFence);
+
+    // Prevent tracing the same release multiple times.
+    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
+        mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
+                                           std::make_shared<FenceTime>(releaseFence),
+                                           FrameTracer::FrameEvent::RELEASE_FENCE);
+        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
+    }
 }
 
 void BufferQueueLayer::setTransformHint(uint32_t orientation) const {
@@ -55,16 +66,12 @@
     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);
+        ALOGW("[%s] Failed to obtain occupancy history (%d)", getDebugName(), result);
         return {};
     }
     return history;
 }
 
-bool BufferQueueLayer::getTransformToDisplayInverse() const {
-    return mConsumer->getTransformToDisplayInverse();
-}
-
 void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
     if (!mConsumer->releasePendingBuffer()) {
         return;
@@ -107,12 +114,28 @@
     ALOGW_IF(!isPlausible,
              "[%s] Timestamp %" PRId64 " seems implausible "
              "relative to expectedPresent %" PRId64,
-             mName.string(), addedTime, expectedPresentTime);
+             getDebugName(), addedTime, expectedPresentTime);
+
+    if (!isPlausible) {
+        mFlinger->mTimeStats->incrementBadDesiredPresent(getSequence());
+    }
 
     const bool isDue = addedTime < expectedPresentTime;
     return isDue || !isPlausible;
 }
 
+bool BufferQueueLayer::setFrameRate(FrameRate frameRate) {
+    float oldFrameRate = 0.f;
+    status_t result = mConsumer->getFrameRate(&oldFrameRate);
+    bool frameRateChanged = result < 0 || frameRate.rate != oldFrameRate;
+    mConsumer->setFrameRate(frameRate.rate);
+    return frameRateChanged;
+}
+
+Layer::FrameRate BufferQueueLayer::getFrameRate() const {
+    return FrameRate(mLatchedFrameRate, Layer::FrameRateCompatibility::Default);
+}
+
 // -----------------------------------------------------------------------
 // Interface implementation for BufferLayer
 // -----------------------------------------------------------------------
@@ -134,75 +157,30 @@
         // able to be latched. To avoid this, grab this buffer anyway.
         return true;
     }
-    return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+    const bool fenceSignaled =
+            mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
 }
 
-bool BufferQueueLayer::framePresentTimeIsCurrent() const {
+bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
     Mutex::Autolock lock(mQueueItemLock);
-    return mQueueItems[0].mTimestamp <= mFlinger->getExpectedPresentTime();
+    return mQueueItems[0].mTimestamp <= expectedPresentTime;
 }
 
-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 {
+uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const {
     Mutex::Autolock lock(mQueueItemLock);
     uint64_t frameNumber = mQueueItems[0].mFrameNumber;
 
     // The head of the queue will be dropped if there are signaled and timely frames behind it
-    nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
-
     if (isRemovedFromCurrentState()) {
         expectedPresentTime = 0;
     }
@@ -244,9 +222,9 @@
     if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) {
         // mSidebandStreamChanged was changed to false
         mSidebandStream = mConsumer->getSidebandStream();
-        auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-        layerCompositionState.sidebandStream = mSidebandStream;
-        if (layerCompositionState.sidebandStream != nullptr) {
+        auto* layerCompositionState = editCompositionState();
+        layerCompositionState->sidebandStream = mSidebandStream;
+        if (layerCompositionState->sidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
         }
@@ -261,26 +239,21 @@
     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) {
+status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                                          nsecs_t expectedPresentTime) {
     // 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();
+    const int32_t layerId = getSequence();
     LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions,
-                    getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode,
-                    getTransformToDisplayInverse(), mFreezeGeometryUpdates);
-
-    nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime();
+                    getProducerStickyTransform() != 0, mName, mOverrideScalingMode,
+                    getTransformToDisplayInverse());
 
     if (isRemovedFromCurrentState()) {
         expectedPresentTime = 0;
@@ -318,7 +291,7 @@
         if (queuedBuffer) {
             Mutex::Autolock lock(mQueueItemLock);
             mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber);
             mQueueItems.removeAt(0);
             mQueuedFrames--;
         }
@@ -332,7 +305,8 @@
             Mutex::Autolock lock(mQueueItemLock);
             mQueueItems.clear();
             mQueuedFrames = 0;
-            mFlinger->mTimeStats->onDestroy(layerID);
+            mFlinger->mTimeStats->onDestroy(layerId);
+            mFlinger->mFrameTracer->onDestroy(layerId);
         }
 
         // Once we have hit this state, the shadow queue may no longer
@@ -354,14 +328,18 @@
         // updateTexImage
         while (mQueueItems[0].mFrameNumber != currentFrameNumber) {
             mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage);
-            mFlinger->mTimeStats->removeTimeRecord(layerID, mQueueItems[0].mFrameNumber);
+            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);
+        uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId();
+        mFlinger->mFrameTracer->traceFence(layerId, bufferID, currentFrameNumber,
+                                           mQueueItems[0].mFenceTime,
+                                           FrameTracer::FrameEvent::ACQUIRE_FENCE);
+        mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime);
+        mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime,
+                                               FrameTracer::FrameEvent::LATCH);
 
         mQueueItems.removeAt(0);
     }
@@ -377,12 +355,13 @@
 
 status_t BufferQueueLayer::updateActiveBuffer() {
     // update the active buffer
-    mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence);
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.bufferSlot = mActiveBufferSlot;
+    mPreviousBufferId = getCurrentBufferId();
+    mBufferInfo.mBuffer =
+            mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
+    auto* layerCompositionState = editCompositionState();
+    layerCompositionState->buffer = mBufferInfo.mBuffer;
 
-    if (mActiveBuffer == nullptr) {
+    if (mBufferInfo.mBuffer == nullptr) {
         // this can only happen if the very first buffer was rejected.
         return BAD_VALUE;
     }
@@ -400,55 +379,55 @@
     return NO_ERROR;
 }
 
-void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    LOG_FATAL_IF(!outputLayer->getState.hwc);
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
+void BufferQueueLayer::preparePerFrameCompositionState() {
+    BufferLayer::preparePerFrameCompositionState();
 
-    uint32_t hwcSlot = 0;
-    sp<GraphicBuffer> hwcBuffer;
-
-    // INVALID_BUFFER_SLOT is used to identify BufferStateLayers.  Default to 0
-    // for BufferQueueLayers
-    int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot;
-    (*outputLayer->editState().hwc)
-            .hwcBufferCache.getHwcBuffer(slot, 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(), mActiveBuffer->handle,
-              to_string(error).c_str(), static_cast<int32_t>(error));
+    auto* compositionState = editCompositionState();
+    if (compositionState->compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
+        return;
     }
 
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.bufferSlot = mActiveBufferSlot;
-    layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.acquireFence = acquireFence;
+    compositionState->buffer = mBufferInfo.mBuffer;
+    compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT)
+            ? 0
+            : mBufferInfo.mBufferSlot;
+    compositionState->acquireFence = mBufferInfo.mFence;
 }
 
 // -----------------------------------------------------------------------
 // Interface implementation for BufferLayerConsumer::ContentsChangedListener
 // -----------------------------------------------------------------------
 
-void BufferQueueLayer::fakeVsync() {
-    mRefreshPending = false;
-    bool ignored = false;
-    latchBuffer(ignored, systemTime());
-    usleep(16000);
-    releasePendingBuffer(systemTime());
+void BufferQueueLayer::onFrameDequeued(const uint64_t bufferId) {
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+                                           systemTime(), FrameTracer::FrameEvent::DEQUEUE);
+}
+
+void BufferQueueLayer::onFrameDetached(const uint64_t bufferId) {
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+                                           systemTime(), FrameTracer::FrameEvent::DETACH);
+}
+
+void BufferQueueLayer::onFrameCancelled(const uint64_t bufferId) {
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, FrameTracer::UNSPECIFIED_FRAME_NUMBER,
+                                           systemTime(), FrameTracer::FrameEvent::CANCEL);
 }
 
 void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
+                                           systemTime(), FrameTracer::FrameEvent::QUEUE);
+
     ATRACE_CALL();
     // Add this buffer from our internal queue tracker
     { // Autolock scope
-        if (mFlinger->mUseSmart90ForVideo) {
-            const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
-            mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
-                                                            item.mHdrMetadata.validTypes != 0);
-        }
+        const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
+        mFlinger->mScheduler->recordLayerHistory(this, presentTime);
 
         Mutex::Autolock lock(mQueueItemLock);
         // Reset the frame number tracker when we receive the first buffer after
@@ -461,7 +440,7 @@
         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());
+                ALOGE("[%s] Timed out waiting on callback", getDebugName());
                 break;
             }
         }
@@ -477,13 +456,7 @@
     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();
-    }
+    mFlinger->signalLayerUpdate();
     mConsumer->onBufferAvailable(item);
 }
 
@@ -496,7 +469,7 @@
         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());
+                ALOGE("[%s] Timed out waiting on callback", getDebugName());
                 break;
             }
         }
@@ -511,6 +484,10 @@
         mLastFrameNumberReceived = item.mFrameNumber;
         mQueueItemCondition.broadcast();
     }
+
+    const int32_t layerId = getSequence();
+    mFlinger->mFrameTracer->traceTimestamp(layerId, item.mGraphicBuffer->getId(), item.mFrameNumber,
+                                           systemTime(), FrameTracer::FrameEvent::QUEUE);
     mConsumer->onBufferAvailable(item);
 }
 
@@ -530,24 +507,23 @@
     // 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);
-    }
+    mFlinger->getFactory().createBufferQueue(&producer, &consumer, true);
+    mProducer = mFlinger->getFactory().createMonitoredProducer(producer, mFlinger, this);
+    mConsumer =
+            mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
+                                                             mTextureName, this);
     mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
-    mConsumer->setContentsChangedListener(this);
-    mConsumer->setName(mName);
+
+    mContentsChangedListener = new ContentsChangedListener(this);
+    mConsumer->setContentsChangedListener(mContentsChangedListener);
+    mConsumer->setName(String8(mName.data(), mName.size()));
 
     // BufferQueueCore::mMaxDequeuedBufferCount is default to 1
     if (!mFlinger->isLayerTripleBufferingDisabled()) {
         mProducer->setMaxDequeuedBufferCount(2);
     }
 
-    if (const auto display = mFlinger->getDefaultDisplayDevice()) {
+    if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) {
         updateTransformHint(display);
     }
 }
@@ -587,4 +563,87 @@
     return static_cast<uint32_t>(producerStickyTransform);
 }
 
+void BufferQueueLayer::gatherBufferInfo() {
+    mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp();
+    mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime();
+    mBufferInfo.mFence = mConsumer->getCurrentFence();
+    mBufferInfo.mTransform = mConsumer->getCurrentTransform();
+    mBufferInfo.mDataspace = translateDataspace(mConsumer->getCurrentDataSpace());
+    mBufferInfo.mCrop = mConsumer->getCurrentCrop();
+    mBufferInfo.mScaleMode = mConsumer->getCurrentScalingMode();
+    mBufferInfo.mSurfaceDamage = mConsumer->getSurfaceDamage();
+    mBufferInfo.mHdrMetadata = mConsumer->getCurrentHdrMetadata();
+    mBufferInfo.mApi = mConsumer->getCurrentApi();
+    mBufferInfo.mPixelFormat = mFormat;
+    mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
+    float latchedFrameRate;
+    mConsumer->getFrameRate(&latchedFrameRate);
+    mLatchedFrameRate = latchedFrameRate;
+}
+
+sp<Layer> BufferQueueLayer::createClone() {
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+    args.textureName = mTextureName;
+    sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args);
+    layer->setInitialValuesForClone(this);
+
+    return layer;
+}
+
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameAvailable(item);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameReplaced(item);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onSidebandStreamChanged();
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameDequeued(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameDetached(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) {
+    Mutex::Autolock lock(mMutex);
+    if (mBufferQueueLayer != nullptr) {
+        mBufferQueueLayer->onFrameCancelled(bufferId);
+    }
+}
+
+void BufferQueueLayer::ContentsChangedListener::abandon() {
+    Mutex::Autolock lock(mMutex);
+    mBufferQueueLayer = nullptr;
+}
+
+// -----------------------------------------------------------------------
+
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 7def33a..626af4b 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -29,8 +29,9 @@
  * This also implements onFrameAvailable(), which notifies SurfaceFlinger
  * that new data has arrived.
  */
-class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferQueueLayer : public BufferLayer {
 public:
+    // Only call while mStateLock is held
     explicit BufferQueueLayer(const LayerCreationArgs&);
     ~BufferQueueLayer() override;
 
@@ -38,14 +39,14 @@
     // Interface implementation for Layer
     // -----------------------------------------------------------------------
 public:
+    const char* getType() const override { return "BufferQueueLayer"; }
+
     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;
 
@@ -54,6 +55,10 @@
     int32_t getQueuedFrameCount() const override;
 
     bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
+
+    bool setFrameRate(FrameRate frameRate) override;
+    FrameRate getFrameRate() const override;
+
     // -----------------------------------------------------------------------
 
     // -----------------------------------------------------------------------
@@ -61,23 +66,10 @@
     // -----------------------------------------------------------------------
 public:
     bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent() const override;
+    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) 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;
+    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
     bool getSidebandStreamChanged() const override;
@@ -86,23 +78,47 @@
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
     status_t updateFrameNumber(nsecs_t latchTime) override;
 
-    void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) override;
+    void preparePerFrameCompositionState() override;
+    sp<Layer> createClone() override;
+
+    void onFrameAvailable(const BufferItem& item);
+    void onFrameReplaced(const BufferItem& item);
+    void onSidebandStreamChanged();
+    void onFrameDequeued(const uint64_t bufferId);
+    void onFrameDetached(const uint64_t bufferId);
+    void onFrameCancelled(const uint64_t bufferId);
+
+protected:
+    void gatherBufferInfo() override;
 
     // -----------------------------------------------------------------------
     // Interface implementation for BufferLayerConsumer::ContentsChangedListener
     // -----------------------------------------------------------------------
-protected:
-    void onFrameAvailable(const BufferItem& item) override;
-    void onFrameReplaced(const BufferItem& item) override;
-    void onSidebandStreamChanged() override;
+    class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener {
+    public:
+        ContentsChangedListener(BufferQueueLayer* bufferQueueLayer)
+              : mBufferQueueLayer(bufferQueueLayer) {}
+        void abandon();
+
+    protected:
+        void onFrameAvailable(const BufferItem& item) override;
+        void onFrameReplaced(const BufferItem& item) override;
+        void onSidebandStreamChanged() override;
+        void onFrameDequeued(const uint64_t bufferId) override;
+        void onFrameDetached(const uint64_t bufferId) override;
+        void onFrameCancelled(const uint64_t bufferId) override;
+
+    private:
+        BufferQueueLayer* mBufferQueueLayer = nullptr;
+        Mutex mMutex;
+    };
     // -----------------------------------------------------------------------
 
 public:
@@ -121,10 +137,11 @@
 
     PixelFormat mFormat{PIXEL_FORMAT_NONE};
 
-    // Only accessed on the main thread.
-    uint64_t mPreviousFrameNumber{0};
     bool mUpdateTexImageFailed{false};
 
+    uint64_t mPreviousBufferId = 0;
+    uint64_t mPreviousReleasedFrameNumber = 0;
+
     // Local copy of the queued contents of the incoming BufferQueue
     mutable Mutex mQueueItemLock;
     Condition mQueueItemCondition;
@@ -132,13 +149,14 @@
     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();
+    sp<ContentsChangedListener> mContentsChangedListener;
+
+    std::atomic<float> mLatchedFrameRate = 0.f;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index 63a07c3..de5429b 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -14,24 +14,26 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "BufferStateLayer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include "BufferStateLayer.h"
+
 #include <limits>
 
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <gui/BufferQueue.h>
 #include <private/gui/SyncFeatures.h>
 #include <renderengine/Image.h>
 
-#include "BufferStateLayer.h"
-#include "ColorLayer.h"
+#include "EffectLayer.h"
+#include "FrameTracer/FrameTracer.h"
 #include "TimeStats/TimeStats.h"
 
 namespace android {
@@ -49,15 +51,22 @@
       : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) {
     mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
     mCurrentState.dataspace = ui::Dataspace::V0_SRGB;
+    if (const auto display = args.displayDevice) {
+        updateTransformHint(display);
+    }
 }
 
 BufferStateLayer::~BufferStateLayer() {
-    if (mActiveBuffer != nullptr) {
-        // Ensure that mActiveBuffer is uncached from RenderEngine here, as
+    // The original layer and the clone layer share the same texture and buffer. Therefore, only
+    // one of the layers, in this case the original layer, needs to handle the deletion. The
+    // original layer and the clone should be removed at the same time so there shouldn't be any
+    // issue with the clone layer trying to use the texture.
+    if (mBufferInfo.mBuffer != nullptr && !isClone()) {
+        // Ensure that mBuffer is uncached from RenderEngine here, as
         // RenderEngine may have been using the buffer as an external texture
         // after the client uncached the buffer.
         auto& engine(mFlinger->getRenderEngine());
-        engine.unbindExternalTextureBuffer(mActiveBuffer->getId());
+        engine.unbindExternalTextureBuffer(mBufferInfo.mBuffer->getId());
     }
 }
 
@@ -87,18 +96,50 @@
             break;
         }
     }
+
+    mPreviousReleaseFence = releaseFence;
+
+    // Prevent tracing the same release multiple times.
+    if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) {
+        mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber,
+                                           std::make_shared<FenceTime>(releaseFence),
+                                           FrameTracer::FrameEvent::RELEASE_FENCE);
+        mPreviousReleasedFrameNumber = mPreviousFrameNumber;
+    }
 }
 
-void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const {
-    // TODO(marissaw): send the transform hint to buffer owner
-    return;
+void BufferStateLayer::setTransformHint(uint32_t orientation) const {
+    mTransformHint = orientation;
 }
 
-void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) {
-    mFlinger->getTransactionCompletedThread().addPresentedCallbackHandles(
+void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->transformHint = mTransformHint;
+        handle->dequeueReadyTime = dequeueReadyTime;
+    }
+
+    mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
             mDrawingState.callbackHandles);
 
     mDrawingState.callbackHandles = {};
+
+    const sp<Fence>& releaseFence(mPreviousReleaseFence);
+    std::shared_ptr<FenceTime> releaseFenceTime = std::make_shared<FenceTime>(releaseFence);
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        if (mPreviousFrameNumber != 0) {
+            mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime,
+                                          std::move(releaseFenceTime));
+        }
+    }
+}
+
+void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
+                                                 const CompositorTiming& compositorTiming) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->gpuCompositionDoneFence = glDoneFence;
+        handle->compositorTiming = compositorTiming;
+    }
 }
 
 bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const {
@@ -111,29 +152,27 @@
 
 bool BufferStateLayer::willPresentCurrentTransaction() const {
     // Returns true if the most recent Transaction applied to CurrentState will be presented.
-    return getSidebandStreamChanged() || getAutoRefresh() ||
+    return (getSidebandStreamChanged() || getAutoRefresh() ||
             (mCurrentState.modified &&
-             (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr));
+             (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr))) &&
+        !mLayerDetached;
 }
 
-bool BufferStateLayer::getTransformToDisplayInverse() const {
-    return mCurrentState.transformToDisplayInverse;
-}
-
+/* TODO: vhau uncomment once deferred transaction migration complete in
+ * WindowManager
 void BufferStateLayer::pushPendingState() {
     if (!mCurrentState.modified) {
         return;
     }
     mPendingStates.push_back(mCurrentState);
-    ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
+*/
 
 bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) {
-    const bool stateUpdateAvailable = !mPendingStates.empty();
-    while (!mPendingStates.empty()) {
-        popPendingState(stateToCommit);
-    }
-    mCurrentStateModified = stateUpdateAvailable && mCurrentState.modified;
+    mCurrentStateModified = mCurrentState.modified;
+    bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit);
+    mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified;
     mCurrentState.modified = false;
     return stateUpdateAvailable;
 }
@@ -215,26 +254,45 @@
     return true;
 }
 
-bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTime,
-                                 nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) {
+bool BufferStateLayer::addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                                     nsecs_t desiredPresentTime) {
+    Mutex::Autolock lock(mFrameEventHistoryMutex);
+    mAcquireTimeline.updateSignalTimes();
+    std::shared_ptr<FenceTime> acquireFenceTime =
+            std::make_shared<FenceTime>((acquireFence ? acquireFence : Fence::NO_FENCE));
+    NewFrameEventsEntry newTimestamps = {mCurrentState.frameNumber, postedTime, desiredPresentTime,
+                                         acquireFenceTime};
+    mFrameEventHistory.setProducerWantsEvents();
+    mFrameEventHistory.addQueue(newTimestamps);
+    return true;
+}
+
+bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence,
+                                 nsecs_t postTime, nsecs_t desiredPresentTime,
+                                 const client_cache_t& clientCacheId) {
     if (mCurrentState.buffer) {
         mReleasePreviousBuffer = true;
     }
 
+    mCurrentState.frameNumber++;
+
     mCurrentState.buffer = buffer;
     mCurrentState.clientCacheId = clientCacheId;
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
 
-    mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime);
-    mDesiredPresentTime = desiredPresentTime;
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(),
+                                      postTime);
+    mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
+    mFlinger->mFrameTracer->traceTimestamp(layerId, buffer->getId(), mCurrentState.frameNumber,
+                                           postTime, FrameTracer::FrameEvent::POST);
+    desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime;
+    mCurrentState.desiredPresentTime = desiredPresentTime;
 
-    if (mFlinger->mUseSmart90ForVideo) {
-        const nsecs_t presentTime = (mDesiredPresentTime == -1) ? 0 : mDesiredPresentTime;
-        mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime,
-                                                        mCurrentState.hdrMetadata.validTypes != 0);
-    }
+    mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
 
+    addFrameEvent(acquireFence, postTime, desiredPresentTime);
     return true;
 }
 
@@ -320,7 +378,7 @@
 
         } 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);
+            mFlinger->getTransactionCompletedThread().registerUnpresentedCallbackHandle(handle);
         }
     }
 
@@ -330,6 +388,11 @@
     return willPresent;
 }
 
+void BufferStateLayer::forceSendCallbacks() {
+    mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles(
+            mCurrentState.callbackHandles);
+}
+
 bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) {
     mCurrentState.transparentRegionHint = transparent;
     mCurrentState.modified = true;
@@ -376,88 +439,59 @@
         return true;
     }
 
-    return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    const bool fenceSignaled =
+            getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled;
+    if (!fenceSignaled) {
+        mFlinger->mTimeStats->incrementLatchSkipped(getSequence(),
+                                                    TimeStats::LatchSkipReason::LateAcquire);
+    }
+
+    return fenceSignaled;
 }
 
-bool BufferStateLayer::framePresentTimeIsCurrent() const {
+bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const {
     if (!hasFrameUpdate() || isRemovedFromCurrentState()) {
         return true;
     }
 
-    return mDesiredPresentTime <= mFlinger->getExpectedPresentTime();
+    return mCurrentState.desiredPresentTime <= expectedPresentTime;
 }
 
-nsecs_t BufferStateLayer::getDesiredPresentTime() {
-    return mDesiredPresentTime;
-}
-
-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;
+bool BufferStateLayer::onPreComposition(nsecs_t refreshStartTime) {
+    for (const auto& handle : mDrawingState.callbackHandles) {
+        handle->refreshStartTime = refreshStartTime;
     }
-    return s.crop;
+    return BufferLayer::onPreComposition(refreshStartTime);
 }
 
-uint32_t BufferStateLayer::getDrawingScalingMode() const {
-    return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const {
+    return mDrawingState.frameNumber;
 }
 
-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;
+/**
+ * This is the frameNumber used for deferred transaction signalling. We need to use this because
+ * of cases where we defer a transaction for a surface to itself. In the BLAST world this
+ * may not make a huge amount of sense (Why not just merge the Buffer transaction with the
+ * deferred transaction?) but this is an important legacy use case, for example moving
+ * a window at the same time it draws makes use of this kind of technique. So anyway
+ * imagine we have something like this:
+ *
+ * Transaction { // containing
+ *     Buffer -> frameNumber = 2
+ *     DeferTransactionUntil -> frameNumber = 2
+ *     Random other stuff
+ *  }
+ * Now imagine getHeadFrameNumber returned mDrawingState.mFrameNumber (or mCurrentFrameNumber).
+ * Prior to doTransaction SurfaceFlinger will call notifyAvailableFrames, but because we
+ * haven't swapped mCurrentState to mDrawingState yet we will think the sync point
+ * is not ready. So we will return false from applyPendingState and not swap
+ * current state to drawing state. But because we don't swap current state
+ * to drawing state the number will never update and we will be stuck. This way
+ * we can see we need to return the frame number for the buffer we are about
+ * to apply.
+ */
+uint64_t BufferStateLayer::getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const {
+    return mCurrentState.frameNumber;
 }
 
 bool BufferStateLayer::getAutoRefresh() const {
@@ -473,9 +507,8 @@
     if (mSidebandStreamChanged.exchange(false)) {
         const State& s(getDrawingState());
         // mSidebandStreamChanged was true
-        LOG_ALWAYS_FATAL_IF(!getCompositionLayer());
         mSidebandStream = s.sidebandStream;
-        getCompositionLayer()->editState().frontEnd.sidebandStream = mSidebandStream;
+        editCompositionState()->sidebandStream = mSidebandStream;
         if (mSidebandStream != nullptr) {
             setTransactionFlags(eTransactionNeeded);
             mFlinger->setTransactionFlags(eTraversalNeeded);
@@ -492,11 +525,6 @@
     return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != 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());
@@ -504,7 +532,8 @@
     return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence);
 }
 
-status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) {
+status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime,
+                                          nsecs_t /*expectedPresentTime*/) {
     const State& s(getDrawingState());
 
     if (!s.buffer) {
@@ -516,7 +545,7 @@
         return NO_ERROR;
     }
 
-    const int32_t layerID = getSequence();
+    const int32_t layerId = getSequence();
 
     // Reject if the layer is invalid
     uint32_t bufferWidth = s.buffer->width;
@@ -527,7 +556,7 @@
     }
 
     if (s.transformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufferWidth, bufferHeight);
         }
@@ -537,13 +566,14 @@
         (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());
+              getDebugName(), bufferWidth, bufferHeight, s.active.w, s.active.h);
+        mFlinger->mTimeStats->removeTimeRecord(layerId, mDrawingState.frameNumber);
         return BAD_VALUE;
     }
 
     for (auto& handle : mDrawingState.callbackHandles) {
         handle->latchTime = latchTime;
+        handle->frameNumber = mDrawingState.frameNumber;
     }
 
     if (!SyncFeatures::getInstance().useNativeFenceSync()) {
@@ -555,13 +585,21 @@
         // a GL-composited layer) not at all.
         status_t err = bindTextureImage();
         if (err != NO_ERROR) {
-            mFlinger->mTimeStats->onDestroy(layerID);
+            mFlinger->mTimeStats->onDestroy(layerId);
+            mFlinger->mFrameTracer->onDestroy(layerId);
             return BAD_VALUE;
         }
     }
 
-    mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime());
-    mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime);
+    const uint64_t bufferID = getCurrentBufferId();
+    mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber,
+                                          mBufferInfo.mFenceTime);
+    mFlinger->mFrameTracer->traceFence(layerId, bufferID, mDrawingState.frameNumber,
+                                       mBufferInfo.mFenceTime,
+                                       FrameTracer::FrameEvent::ACQUIRE_FENCE);
+    mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime);
+    mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, mDrawingState.frameNumber, latchTime,
+                                           FrameTracer::FrameEvent::LATCH);
 
     mCurrentStateModified = false;
 
@@ -575,49 +613,36 @@
         return BAD_VALUE;
     }
 
-    mActiveBuffer = s.buffer;
-    mActiveBufferFence = s.acquireFence;
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.buffer = mActiveBuffer;
-    layerCompositionState.bufferSlot = 0;
+    mPreviousBufferId = getCurrentBufferId();
+    mBufferInfo.mBuffer = s.buffer;
+    mBufferInfo.mFence = s.acquireFence;
+    editCompositionState()->buffer = mBufferInfo.mBuffer;
 
     return NO_ERROR;
 }
 
-status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) {
+status_t BufferStateLayer::updateFrameNumber(nsecs_t latchTime) {
     // TODO(marissaw): support frame history events
-    mCurrentFrameNumber = mFrameNumber;
+    mPreviousFrameNumber = mCurrentFrameNumber;
+    mCurrentFrameNumber = mDrawingState.frameNumber;
+    {
+        Mutex::Autolock lock(mFrameEventHistoryMutex);
+        mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime);
+    }
     return NO_ERROR;
 }
 
-void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-    auto& hwcInfo = *outputLayer->editState().hwc;
-    auto& hwcLayer = hwcInfo.hwcLayer;
+void BufferStateLayer::preparePerFrameCompositionState() {
+    BufferLayer::preparePerFrameCompositionState();
 
-    const State& s(getDrawingState());
-
-    uint32_t hwcSlot;
-    sp<GraphicBuffer> buffer;
-    hwcInfo.hwcBufferCache.getHwcBuffer(mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId),
-                                        s.buffer, &hwcSlot, &buffer);
-
-    auto error = hwcLayer->setBuffer(hwcSlot, 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));
+    auto* compositionState = editCompositionState();
+    if (compositionState->compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) {
+        return;
     }
 
-    mFrameNumber++;
-}
-
-void BufferStateLayer::onFirstRef() {
-    BufferLayer::onFirstRef();
-
-    if (const auto display = mFlinger->getDefaultDisplayDevice()) {
-        updateTransformHint(display);
-    }
+    compositionState->buffer = mBufferInfo.mBuffer;
+    compositionState->bufferSlot = mBufferInfo.mBufferSlot;
+    compositionState->acquireFence = mBufferInfo.mFence;
 }
 
 void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) {
@@ -692,4 +717,85 @@
     mFreeHwcCacheSlots.push(hwcCacheSlot);
     mCachedBuffers.erase(clientCacheId);
 }
+
+void BufferStateLayer::gatherBufferInfo() {
+    const State& s(getDrawingState());
+
+    mBufferInfo.mDesiredPresentTime = s.desiredPresentTime;
+    mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence);
+    mBufferInfo.mFence = s.acquireFence;
+    mBufferInfo.mTransform = s.transform;
+    mBufferInfo.mDataspace = translateDataspace(s.dataspace);
+    mBufferInfo.mCrop = computeCrop(s);
+    mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
+    mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion;
+    mBufferInfo.mHdrMetadata = s.hdrMetadata;
+    mBufferInfo.mApi = s.api;
+    mBufferInfo.mPixelFormat =
+            !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format;
+    mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse;
+    mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId);
+}
+
+Rect BufferStateLayer::computeCrop(const State& s) {
+    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;
+}
+
+sp<Layer> BufferStateLayer::createClone() {
+    LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+    args.textureName = mTextureName;
+    sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
+    layer->mHwcSlotGenerator = mHwcSlotGenerator;
+    layer->setInitialValuesForClone(this);
+    return layer;
+}
+
+Layer::RoundedCornerState BufferStateLayer::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;
+    const State& s(getDrawingState());
+    if (radius <= 0 || (getActiveWidth(s) == UINT32_MAX && getActiveHeight(s) == UINT32_MAX))
+        return RoundedCornerState();
+    return RoundedCornerState(FloatRect(static_cast<float>(s.active.transform.tx()),
+                                        static_cast<float>(s.active.transform.ty()),
+                                        static_cast<float>(s.active.transform.tx() + s.active.w),
+                                        static_cast<float>(s.active.transform.ty() + s.active.h)),
+                              radius);
+}
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h
index 97e24cb..753a742 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -19,7 +19,6 @@
 #include "BufferLayer.h"
 #include "Layer.h"
 
-#include <gui/GLConsumer.h>
 #include <renderengine/Image.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
@@ -40,18 +39,23 @@
     // -----------------------------------------------------------------------
     // Interface implementation for Layer
     // -----------------------------------------------------------------------
+    const char* getType() const override { return "BufferStateLayer"; }
+
     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;
+    void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence,
+                                   const CompositorTiming& compositorTiming) override;
 
-    bool getTransformToDisplayInverse() const override;
+    bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
 
     uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override {
         return flags;
     }
-    void pushPendingState() override;
+    /*TODO:vhau return to using BufferStateLayer override once WM
+     * has removed deferred transactions!
+    void pushPendingState() override;*/
     bool applyPendingStates(Layer::State* stateToCommit) override;
 
     uint32_t getActiveWidth(const Layer::State& s) const override { return s.active.w; }
@@ -64,12 +68,13 @@
     }
     Rect getCrop(const Layer::State& s) const;
 
+    uint32_t getTransformHint() const { return mTransformHint; }
     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, nsecs_t postTime, nsecs_t desiredPresentTime,
-                   const client_cache_t& clientCacheId) override;
+    bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime,
+                   nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override;
     bool setAcquireFence(const sp<Fence>& fence) override;
     bool setDataspace(ui::Dataspace dataspace) override;
     bool setHdrMetadata(const HdrMetadata& hdrMetadata) override;
@@ -77,16 +82,19 @@
     bool setApi(int32_t api) override;
     bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override;
     bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override;
+    void forceSendCallbacks() override;
+    bool addFrameEvent(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                       nsecs_t requestedPresentTime) 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 setPosition(float /*x*/, float /*y*/) 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 setCrop_legacy(const Rect& /*crop*/) override { return false; }
     bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; }
     void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/,
                                       uint64_t /*frameNumber*/) override {}
@@ -95,6 +103,7 @@
 
     Rect getBufferSize(const State& s) const override;
     FloatRect computeSourceBounds(const FloatRect& parentBounds) const override;
+    Layer::RoundedCornerState getRoundedCornerState() const override;
 
     // -----------------------------------------------------------------------
 
@@ -102,23 +111,18 @@
     // Interface implementation for BufferLayer
     // -----------------------------------------------------------------------
     bool fenceHasSignaled() const override;
-    bool framePresentTimeIsCurrent() const override;
+    bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override;
+    bool onPreComposition(nsecs_t refreshStartTime) override;
+
+protected:
+    void gatherBufferInfo() override;
+    uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const;
 
 private:
-    nsecs_t getDesiredPresentTime() override;
-    std::shared_ptr<FenceTime> getCurrentFenceTime() const override;
+    bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime,
+                                 nsecs_t requestedPresentTime);
 
-    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;
+    uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override;
 
     bool getAutoRefresh() const override;
     bool getSidebandStreamChanged() const override;
@@ -127,38 +131,41 @@
 
     bool hasFrameUpdate() const override;
 
-    void setFilteringEnabled(bool enabled) override;
-
     status_t bindTextureImage() override;
-    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override;
+    status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
+                            nsecs_t expectedPresentTime) override;
 
     status_t updateActiveBuffer() override;
     status_t updateFrameNumber(nsecs_t latchTime) override;
 
-    void setHwcLayerBuffer(const sp<const DisplayDevice>& display) override;
+    void preparePerFrameCompositionState() override;
+    sp<Layer> createClone() override;
+
+    // Crop that applies to the buffer
+    Rect computeCrop(const State& s);
 
 private:
     friend class SlotGenerationTest;
-    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};
+    mutable uint64_t mFrameNumber{0};
+    uint64_t mFrameCounter{0};
 
     sp<Fence> mPreviousReleaseFence;
+    uint64_t mPreviousBufferId = 0;
+    uint64_t mPreviousReleasedFrameNumber = 0;
 
-    bool mCurrentStateModified = false;
+    mutable bool mCurrentStateModified = false;
     bool mReleasePreviousBuffer = false;
     nsecs_t mCallbackHandleAcquireTime = -1;
 
-    nsecs_t mDesiredPresentTime = -1;
+    mutable uint32_t mTransformHint = 0;
 
     // TODO(marissaw): support sticky transform for LEGACY camera mode
 
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 6bfd302..fb72ab8 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <stdint.h>
 #include <sys/types.h>
 
@@ -75,17 +79,18 @@
 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<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) {
     // We rely on createLayer to check permissions.
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 parentHandle);
+                                 parentHandle, nullptr, outTransformHint);
 }
 
 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) {
+                                         sp<IGraphicBufferProducer>* gbp,
+                                         uint32_t* outTransformHint) {
     if (mFlinger->authenticateSurfaceTexture(parent) == false) {
         ALOGE("failed to authenticate surface texture");
         // The extra parent layer check below before returning is to help with debugging
@@ -103,7 +108,11 @@
     }
 
     return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
-                                 nullptr, layer);
+                                 nullptr, layer, outTransformHint);
+}
+
+status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) {
+    return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle);
 }
 
 status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
@@ -126,3 +135,6 @@
 
 // ---------------------------------------------------------------------------
 }; // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h
index 74e4818..e9063e5 100644
--- a/services/surfaceflinger/Client.h
+++ b/services/surfaceflinger/Client.h
@@ -54,13 +54,17 @@
     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);
+                                   sp<IGraphicBufferProducer>* gbp,
+                                   uint32_t* outTransformHint = nullptr);
 
     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);
+                                             sp<IGraphicBufferProducer>* gbp,
+                                             uint32_t* outTransformHint = nullptr);
+
+    status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle);
 
     virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const;
 
diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp
index 16fe27c..a5be01c 100644
--- a/services/surfaceflinger/ClientCache.cpp
+++ b/services/surfaceflinger/ClientCache.cpp
@@ -42,7 +42,7 @@
         return false;
     }
 
-    auto& processBuffers = it->second;
+    auto& processBuffers = it->second.second;
 
     auto bufItr = processBuffers.find(id);
     if (bufItr == processBuffers.end()) {
@@ -86,12 +86,14 @@
             return false;
         }
         auto [itr, success] =
-                mBuffers.emplace(processToken, std::unordered_map<uint64_t, ClientCacheBuffer>());
+                mBuffers.emplace(processToken,
+                                 std::make_pair(token,
+                                                std::unordered_map<uint64_t, ClientCacheBuffer>()));
         LOG_ALWAYS_FATAL_IF(!success, "failed to insert new process into client cache");
         it = itr;
     }
 
-    auto& processBuffers = it->second;
+    auto& processBuffers = it->second.second;
 
     if (processBuffers.size() > BUFFER_CACHE_MAX_SIZE) {
         ALOGE("failed to cache buffer: cache is full");
@@ -120,7 +122,7 @@
             }
         }
 
-        mBuffers[processToken].erase(id);
+        mBuffers[processToken].second.erase(id);
     }
 
     for (auto& recipient : pendingErase) {
@@ -180,7 +182,7 @@
             return;
         }
 
-        for (auto& [id, clientCacheBuffer] : itr->second) {
+        for (auto& [id, clientCacheBuffer] : itr->second.second) {
             client_cache_t cacheId = {processToken, id};
             for (auto& recipient : clientCacheBuffer.recipients) {
                 sp<ErasedRecipient> erasedRecipient = recipient.promote();
diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h
index aa6c80d..d7af7c0 100644
--- a/services/surfaceflinger/ClientCache.h
+++ b/services/surfaceflinger/ClientCache.h
@@ -61,7 +61,8 @@
         std::set<wp<ErasedRecipient>> recipients;
     };
     std::map<wp<IBinder> /*caching process*/,
-             std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>
+             std::pair<sp<IBinder> /*strong ref to caching process*/,
+                       std::unordered_map<uint64_t /*cache id*/, ClientCacheBuffer>>>
             mBuffers GUARDED_BY(mMutex);
 
     class CacheDeathRecipient : public IBinder::DeathRecipient {
diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp
deleted file mode 100644
index fcc2d97..0000000
--- a/services/surfaceflinger/ColorLayer.cpp
+++ /dev/null
@@ -1,176 +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.
- */
-
-// #define LOG_NDEBUG 0
-#undef LOG_TAG
-#define LOG_TAG "ColorLayer"
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
-#include <compositionengine/LayerCreationArgs.h>
-#include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
-#include <renderengine/RenderEngine.h>
-#include <ui/GraphicBuffer.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include "ColorLayer.h"
-#include "DisplayDevice.h"
-#include "SurfaceFlinger.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-ColorLayer::ColorLayer(const LayerCreationArgs& args)
-      : Layer(args),
-        mCompositionLayer{mFlinger->getCompositionEngine().createLayer(
-                compositionengine::LayerCreationArgs{this})} {}
-
-ColorLayer::~ColorLayer() = default;
-
-bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    const bool supportProtectedContent,
-                                    renderengine::LayerSettings& layer) {
-    Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion,
-                              supportProtectedContent, layer);
-    half4 color(getColor());
-    half3 solidColor(color.r, color.g, color.b);
-    layer.source.solidColor = solidColor;
-    return true;
-}
-
-bool ColorLayer::isVisible() const {
-    return !isHiddenByPolicy() && getAlpha() > 0.0f;
-}
-
-bool ColorLayer::setColor(const half3& color) {
-    if (mCurrentState.color.r == color.r && mCurrentState.color.g == color.g &&
-        mCurrentState.color.b == color.b) {
-        return false;
-    }
-
-    mCurrentState.sequence++;
-    mCurrentState.color.r = color.r;
-    mCurrentState.color.g = color.g;
-    mCurrentState.color.b = color.b;
-    mCurrentState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-bool ColorLayer::setDataspace(ui::Dataspace dataspace) {
-    if (mCurrentState.dataspace == dataspace) {
-        return false;
-    }
-
-    mCurrentState.sequence++;
-    mCurrentState.dataspace = dataspace;
-    mCurrentState.modified = true;
-    setTransactionFlags(eTransactionNeeded);
-    return true;
-}
-
-void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display,
-                                 const ui::Transform& transform, const Rect& viewport,
-                                 int32_t /* supportedPerFrameMetadata */,
-                                 const ui::Dataspace targetDataspace) {
-    RETURN_IF_NO_HWC_LAYER(display);
-
-    Region visible = transform.transform(visibleRegion.intersect(viewport));
-
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc);
-
-    auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer;
-
-    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);
-    }
-    outputLayer->editState().visibleRegion = visible;
-
-    setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR);
-
-    const ui::Dataspace dataspace =
-            isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN ? targetDataspace
-                                                                                : mCurrentDataSpace;
-    error = hwcLayer->setDataspace(dataspace);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace,
-              to_string(error).c_str(), static_cast<int32_t>(error));
-    }
-
-    auto& layerCompositionState = getCompositionLayer()->editState().frontEnd;
-    layerCompositionState.dataspace = mCurrentDataSpace;
-
-    half4 color = getColor();
-    error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)),
-                                static_cast<uint8_t>(std::round(255.0f * color.g)),
-                                static_cast<uint8_t>(std::round(255.0f * color.b)), 255});
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(),
-              static_cast<int32_t>(error));
-    }
-    layerCompositionState.color = {static_cast<uint8_t>(std::round(255.0f * color.r)),
-                                   static_cast<uint8_t>(std::round(255.0f * color.g)),
-                                   static_cast<uint8_t>(std::round(255.0f * color.b)), 255};
-
-    // Clear out the transform, because it doesn't make sense absent a source buffer
-    error = hwcLayer->setTransform(HWC2::Transform::None);
-    if (error != HWC2::Error::None) {
-        ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(),
-              static_cast<int32_t>(error));
-    }
-    outputLayer->editState().bufferTransform = static_cast<Hwc2::Transform>(0);
-
-    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));
-    }
-    layerCompositionState.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);
-    }
-    layerCompositionState.surfaceDamage = surfaceDamageRegion;
-}
-
-void ColorLayer::commitTransaction(const State& stateToCommit) {
-    Layer::commitTransaction(stateToCommit);
-    mCurrentDataSpace = mDrawingState.dataspace;
-}
-
-std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const {
-    return mCompositionLayer;
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h
deleted file mode 100644
index 53d5b5b..0000000
--- a/services/surfaceflinger/ColorLayer.h
+++ /dev/null
@@ -1,58 +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.
- */
-#pragma once
-
-#include <sys/types.h>
-
-#include <cstdint>
-
-#include "Layer.h"
-
-namespace android {
-
-class ColorLayer : public Layer {
-public:
-    explicit ColorLayer(const LayerCreationArgs&);
-    ~ColorLayer() override;
-
-    std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override;
-
-    virtual const char* getTypeId() const { return "ColorLayer"; }
-    bool isVisible() const override;
-
-    bool setColor(const half3& color) override;
-
-    bool setDataspace(ui::Dataspace dataspace) override;
-
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) override;
-
-    void commitTransaction(const State& stateToCommit) override;
-
-    bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; }
-
-protected:
-    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    const bool supportProtectedContent,
-                                    renderengine::LayerSettings& layer);
-
-private:
-    std::shared_ptr<compositionengine::Layer> mCompositionLayer;
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 1c31ab9..2792290 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -3,13 +3,15 @@
     defaults: ["surfaceflinger_defaults"],
     cflags: [
         "-DLOG_TAG=\"CompositionEngine\"",
+        "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "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.graphics.composer@2.4",
         "android.hardware.power@1.0",
         "android.hardware.power@1.3",
         "libbase",
@@ -18,8 +20,9 @@
         "liblayers_proto",
         "liblog",
         "libnativewindow",
+        "libprotobuf-cpp-lite",
         "libsync",
-        "libtimestats_proto",
+        "libtimestats",
         "libui",
         "libutils",
     ],
@@ -32,6 +35,7 @@
         "android.hardware.graphics.composer@2.1-command-buffer",
         "android.hardware.graphics.composer@2.2-command-buffer",
         "android.hardware.graphics.composer@2.3-command-buffer",
+        "android.hardware.graphics.composer@2.4-command-buffer",
         "libsurfaceflinger_headers",
     ],
 }
@@ -40,14 +44,14 @@
     name: "libcompositionengine",
     defaults: ["libcompositionengine_defaults"],
     srcs: [
+        "src/ClientCompositionRequestCache.cpp",
         "src/CompositionEngine.cpp",
         "src/Display.cpp",
         "src/DisplayColorProfile.cpp",
         "src/DisplaySurface.cpp",
         "src/DumpHelpers.cpp",
         "src/HwcBufferCache.cpp",
-        "src/Layer.cpp",
-        "src/LayerCompositionState.cpp",
+        "src/LayerFECompositionState.cpp",
         "src/Output.cpp",
         "src/OutputCompositionState.cpp",
         "src/OutputLayer.cpp",
@@ -66,7 +70,6 @@
         "mock/Display.cpp",
         "mock/DisplayColorProfile.cpp",
         "mock/DisplaySurface.cpp",
-        "mock/Layer.cpp",
         "mock/LayerFE.cpp",
         "mock/NativeWindow.cpp",
         "mock/Output.cpp",
@@ -91,9 +94,9 @@
         "tests/DisplayColorProfileTest.cpp",
         "tests/DisplayTest.cpp",
         "tests/HwcBufferCacheTest.cpp",
-        "tests/LayerTest.cpp",
         "tests/MockHWC2.cpp",
         "tests/MockHWComposer.cpp",
+        "tests/MockPowerAdvisor.cpp",
         "tests/OutputTest.cpp",
         "tests/OutputLayerTest.cpp",
         "tests/RenderSurfaceTest.cpp",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 896f8aa..3faa068 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -16,6 +16,9 @@
 
 #pragma once
 
+#include <TimeStats/TimeStats.h>
+#include <utils/Timers.h>
+
 #include <memory>
 
 namespace android {
@@ -29,10 +32,11 @@
 namespace compositionengine {
 
 class Display;
-class Layer;
 
+struct CompositionRefreshArgs;
 struct DisplayCreationArgs;
 struct LayerCreationArgs;
+struct LayerFECompositionState;
 
 /**
  * Encapsulates all the interfaces and implementation details for performing
@@ -43,14 +47,33 @@
     virtual ~CompositionEngine();
 
     // Create a composition Display
-    virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0;
-    virtual std::shared_ptr<Layer> createLayer(LayerCreationArgs&&) = 0;
+    virtual std::shared_ptr<Display> createDisplay(const DisplayCreationArgs&) = 0;
+    virtual std::unique_ptr<compositionengine::LayerFECompositionState>
+    createLayerFECompositionState() = 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;
+
+    virtual TimeStats& getTimeStats() const = 0;
+    virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
+
+    virtual bool needsAnotherUpdate() const = 0;
+    virtual nsecs_t getLastFrameRefreshTimestamp() const = 0;
+
+    // Presents the indicated outputs
+    virtual void present(CompositionRefreshArgs&) = 0;
+
+    // Updates the cursor position for the indicated outputs.
+    virtual void updateCursorAsync(CompositionRefreshArgs&) = 0;
+
+    // TODO(b/121291683): These will become private/internal
+    virtual void preComposition(CompositionRefreshArgs&) = 0;
+
+    // Debugging
+    virtual void dump(std::string&) const = 0;
 };
 
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
new file mode 100644
index 0000000..4a0d6ee
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -0,0 +1,77 @@
+/*
+ * 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 <chrono>
+#include <optional>
+#include <vector>
+
+#include <compositionengine/Display.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/OutputColorSetting.h>
+#include <math/mat4.h>
+
+namespace android::compositionengine {
+
+using Layers = std::vector<sp<compositionengine::LayerFE>>;
+using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
+
+/**
+ * A parameter object for refreshing a set of outputs
+ */
+struct CompositionRefreshArgs {
+    // All the outputs being refreshed
+    Outputs outputs;
+
+    // All the layers that are potentially visible in the outputs. The order of
+    // the layers is important, and should be in traversal order from back to
+    // front.
+    Layers layers;
+
+    // All the layers that have queued updates.
+    Layers layersWithQueuedFrames;
+
+    // If true, forces the entire display to be considered dirty and repainted
+    bool repaintEverything{false};
+
+    // Controls how the color mode is chosen for an output
+    OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced};
+
+    // If not Dataspace::UNKNOWN, overrides the dataspace on each output
+    ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
+
+    // Forces a color mode on the outputs being refreshed
+    ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE};
+
+    // If true, the complete output geometry needs to be recomputed this frame
+    bool updatingOutputGeometryThisFrame{false};
+
+    // If true, there was a geometry update this frame
+    bool updatingGeometryThisFrame{false};
+
+    // The color matrix to use for this
+    // frame. Only set if the color transform is changing this frame.
+    std::optional<mat4> colorTransformMatrix;
+
+    // If true, client composition is always used.
+    bool devOptForceClientComposition{false};
+
+    // If set, causes the dirty regions to flash with the delay
+    std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index dbcd3bd..a38d1f3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -47,10 +47,14 @@
     virtual void disconnect() = 0;
 
     // Creates a render color mode for the display
-    virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0;
+    virtual void createDisplayColorProfile(const DisplayColorProfileCreationArgs&) = 0;
 
     // Creates a render surface for the display
-    virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0;
+    virtual void createRenderSurface(const RenderSurfaceCreationArgs&) = 0;
+
+    // Creates a cache to cache duplicate client composition requests and skip
+    // similar requests if needed.
+    virtual void createClientCompositionCache(uint32_t cacheSize) = 0;
 
 protected:
     ~Display() = default;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
index e2a0d42..67e6deb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h
@@ -17,9 +17,17 @@
 #pragma once
 
 #include <cstdint>
+#include <string>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 
 #include <ui/GraphicTypes.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android {
 
 class HdrCapabilities;
@@ -75,6 +83,13 @@
     // Gets the supported HDR capabilities for the profile
     virtual const HdrCapabilities& getHdrCapabilities() const = 0;
 
+    // Returns true if HWC for this profile supports the dataspace
+    virtual bool isDataspaceSupported(ui::Dataspace) const = 0;
+
+    // Returns the target dataspace for picked color mode and dataspace
+    virtual ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace,
+                                             ui::Dataspace colorSpaceAgnosticDataspace) const = 0;
+
     // Debugging
     virtual void dump(std::string&) const = 0;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
index ef0f925..7eb8eb1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfileCreationArgs.h
@@ -20,7 +20,15 @@
 #include <unordered_map>
 #include <vector>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <ui/GraphicTypes.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <ui/HdrCapabilities.h>
 
 namespace android::compositionengine {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index 0b6b4e4..ced4227 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -20,6 +20,7 @@
 #include <optional>
 
 #include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
@@ -29,14 +30,15 @@
  * 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;
+
+    // Optional pointer to the power advisor interface, if one is needed for
+    // this display.
+    Hwc2::PowerAdvisor* powerAdvisor = nullptr;
 };
 
 /**
@@ -49,17 +51,13 @@
  *
  * Prefer:
  *
- *  DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false)
+ *  DisplayCreationArgsBuilder().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;
@@ -68,6 +66,10 @@
         mArgs.displayId = displayId;
         return *this;
     }
+    DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
+        mArgs.powerAdvisor = powerAdvisor;
+        return *this;
+    }
 
 private:
     DisplayCreationArgs mArgs;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 0e67acf..6559ed8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -45,19 +45,19 @@
     // prepareFrame is called after the composition configuration is known but
     // before composition takes place. The DisplaySurface can use the
     // composition type to decide how to manage the flow of buffers between
-    // GLES and HWC for this frame.
+    // GPU and HWC for this frame.
     enum CompositionType {
         COMPOSITION_UNKNOWN = 0,
-        COMPOSITION_GLES = 1,
+        COMPOSITION_GPU = 1,
         COMPOSITION_HWC = 2,
-        COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
+        COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC
     };
     virtual status_t prepareFrame(CompositionType compositionType) = 0;
 
-    // Inform the surface that GLES composition is complete for this frame, and
+    // Inform the surface that GPU composition is complete for this frame, and
     // the surface should make sure that HWComposer has the correct buffer for
     // this frame. Some implementations may only push a new buffer to
-    // HWComposer if GLES composition took place, others need to push a new
+    // HWComposer if GPU composition took place, others need to push a new
     // buffer on every frame.
     //
     // advanceFrame must be followed by a call to  onFrameCommitted before
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
deleted file mode 100644
index 8cb9203..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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 <utils/StrongPointer.h>
-
-namespace android {
-
-typedef int64_t nsecs_t;
-
-namespace compositionengine {
-
-class Display;
-class LayerFE;
-
-namespace impl {
-struct LayerCompositionState;
-} // namespace impl
-
-/**
- * A layer contains the output-independent composition state for a front-end
- * Layer
- */
-class Layer {
-public:
-    virtual ~Layer();
-
-    // Gets the front-end interface for this layer.  Can return nullptr if the
-    // front-end layer no longer exists.
-    virtual sp<LayerFE> getLayerFE() const = 0;
-
-    using CompositionState = impl::LayerCompositionState;
-
-    // Gets the raw composition state data for the layer
-    // TODO(lpique): Make this protected once it is only internally called.
-    virtual const CompositionState& getState() const = 0;
-
-    // Allows mutable access to the raw composition state data for the layer.
-    // 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 CompositionState& editState() = 0;
-
-    // Debugging
-    virtual void dump(std::string& result) const = 0;
-};
-
-} // namespace compositionengine
-} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 9f635b9..6cc90cb 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -16,7 +16,21 @@
 
 #pragma once
 
+#include <optional>
+#include <ostream>
+#include <unordered_set>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <renderengine/LayerSettings.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <utils/RefBase.h>
+#include <utils/Timers.h>
 
 namespace android {
 
@@ -30,9 +44,91 @@
 // of the front-end layer
 class LayerFE : public virtual RefBase {
 public:
-    // Latches the output-independent state. If includeGeometry is false, the
-    // geometry state can be skipped.
-    virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0;
+    // Gets the raw front-end composition state data for the layer
+    virtual const LayerFECompositionState* getCompositionState() const = 0;
+
+    // Called before composition starts. Should return true if this layer has
+    // pending updates which would require an extra display refresh cycle to
+    // process.
+    virtual bool onPreComposition(nsecs_t refreshStartTime) = 0;
+
+    // Used with latchCompositionState()
+    enum class StateSubset {
+        // Gets the basic geometry (bounds, transparent region, visibility,
+        // transforms, alpha) for the layer, for computing visibility and
+        // coverage.
+        BasicGeometry,
+
+        // Gets the full geometry (crops, buffer transforms, metadata) and
+        // content (buffer or color) state for the layer.
+        GeometryAndContent,
+
+        // Gets the per frame content (buffer or color) state for the layer.
+        Content,
+
+        // Gets the cursor state for the layer.
+        Cursor,
+    };
+
+    // Prepares the output-independent composition state for the layer. The
+    // StateSubset argument selects what portion of the state is actually needed
+    // by the CompositionEngine code, since computing everything may be
+    // expensive.
+    virtual void prepareCompositionState(StateSubset) = 0;
+
+    struct ClientCompositionTargetSettings {
+        // The clip region, or visible region that is being rendered to
+        const Region& clip;
+
+        // If true, the layer should use an identity transform for its position
+        // transform. Used only by the captureScreen API call.
+        const bool useIdentityTransform;
+
+        // If set to true, the layer should enable filtering when rendering.
+        const bool needsFiltering;
+
+        // If set to true, the buffer is being sent to a destination that is
+        // expected to treat the buffer contents as secure.
+        const bool isSecure;
+
+        // If set to true, the target buffer has protected content support.
+        const bool supportsProtectedContent;
+
+        // Modified by each call to prepareClientComposition to indicate the
+        // region of the target buffer that should be cleared.
+        Region& clearRegion;
+
+        // Viewport of the target being rendered to. This is used to determine
+        // the shadow light position.
+        const Rect& viewport;
+
+        // Dataspace of the output so we can optimize how to render the shadow
+        // by avoiding unnecessary color space conversions.
+        const ui::Dataspace dataspace;
+
+        // True if the region excluding the shadow is visible.
+        const bool realContentIsVisible;
+
+        // If set to true, change the layer settings to render a clear output.
+        // This may be requested by the HWC
+        const bool clearContent;
+    };
+
+    // A superset of LayerSettings required by RenderEngine to compose a layer
+    // and buffer info to determine duplicate client composition requests.
+    struct LayerSettings : renderengine::LayerSettings {
+        // Currently latched buffer if, 0 if invalid.
+        uint64_t bufferId = 0;
+
+        // Currently latched frame number, 0 if invalid.
+        uint64_t frameNumber = 0;
+    };
+
+    // Returns the z-ordered list of LayerSettings to pass to RenderEngine::drawLayers. The list
+    // may contain shadows casted by the layer or the content of the layer itself.  If the layer
+    // does not render then an empty list will be returned.
+    virtual std::vector<LayerSettings> prepareClientCompositionList(
+            ClientCompositionTargetSettings&) = 0;
 
     // Called after the layer is displayed to update the presentation fence
     virtual void onLayerDisplayed(const sp<Fence>&) = 0;
@@ -41,5 +137,61 @@
     virtual const char* getDebugName() const = 0;
 };
 
+// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can
+// be removed.
+struct LayerFESpHash {
+    size_t operator()(const sp<LayerFE>& p) const { return std::hash<LayerFE*>()(p.get()); }
+};
+
+using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>;
+
+static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
+                              const LayerFE::ClientCompositionTargetSettings& rhs) {
+    return lhs.clip.hasSameRects(rhs.clip) &&
+            lhs.useIdentityTransform == rhs.useIdentityTransform &&
+            lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
+            lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
+            lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport &&
+            lhs.dataspace == rhs.dataspace &&
+            lhs.realContentIsVisible == rhs.realContentIsVisible &&
+            lhs.clearContent == rhs.clearContent;
+}
+
+static inline bool operator==(const LayerFE::LayerSettings& lhs,
+                              const LayerFE::LayerSettings& rhs) {
+    return static_cast<const renderengine::LayerSettings&>(lhs) ==
+            static_cast<const renderengine::LayerSettings&>(rhs) &&
+            lhs.bufferId == rhs.bufferId && lhs.frameNumber == rhs.frameNumber;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& settings,
+                           ::std::ostream* os) {
+    *os << "ClientCompositionTargetSettings{";
+    *os << "\n    .clip = \n";
+    PrintTo(settings.clip, os);
+    *os << "\n    .useIdentityTransform = " << settings.useIdentityTransform;
+    *os << "\n    .needsFiltering = " << settings.needsFiltering;
+    *os << "\n    .isSecure = " << settings.isSecure;
+    *os << "\n    .supportsProtectedContent = " << settings.supportsProtectedContent;
+    *os << "\n    .clearRegion = ";
+    PrintTo(settings.clearRegion, os);
+    *os << "\n    .viewport = ";
+    PrintTo(settings.viewport, os);
+    *os << "\n    .dataspace = ";
+    PrintTo(settings.dataspace, os);
+    *os << "\n    .realContentIsVisible = " << settings.realContentIsVisible;
+    *os << "\n    .clearContent = " << settings.clearContent;
+    *os << "\n}";
+}
+
+static inline void PrintTo(const LayerFE::LayerSettings& settings, ::std::ostream* os) {
+    *os << "LayerFE::LayerSettings{";
+    PrintTo(static_cast<const renderengine::LayerSettings&>(settings), os);
+    *os << "\n    .bufferId = " << settings.bufferId;
+    *os << "\n    .frameNumber = " << settings.frameNumber;
+    *os << "\n}";
+}
+
 } // namespace compositionengine
 } // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index e6ee078..d8ce629 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -18,27 +18,103 @@
 
 #include <cstdint>
 
-#include <gui/BufferQueue.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.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>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferQueue.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+
 #include "DisplayHardware/ComposerHal.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine {
 
+// More complex metadata for this layer
+struct GenericLayerMetadataEntry {
+    // True if the metadata may affect the composed result.
+    // See setLayerGenericMetadata in IComposerClient.hal
+    bool mandatory;
+
+    // Byte blob or parcel
+    std::vector<uint8_t> value;
+
+    std::string dumpAsString() const;
+};
+
+inline bool operator==(const GenericLayerMetadataEntry& lhs, const GenericLayerMetadataEntry& rhs) {
+    return lhs.mandatory == rhs.mandatory && lhs.value == rhs.value;
+}
+
+// Defining PrintTo helps with Google Tests.
+inline void PrintTo(const GenericLayerMetadataEntry& v, ::std::ostream* os) {
+    *os << v.dumpAsString();
+}
+
+using GenericLayerMetadataMap = std::unordered_map<std::string, GenericLayerMetadataEntry>;
+
 /*
  * Used by LayerFE::getCompositionState
  */
 struct LayerFECompositionState {
-    // TODO(lpique): b/121291683 Remove this one we are sure we don't need the
-    // value recomputed / set every frame.
-    Region geomVisibleRegion;
+    // If set to true, forces client composition on all output layers until
+    // the next geometry change.
+    bool forceClientComposition{false};
+
+    // TODO(b/121291683): Reorganize and rename the contents of this structure
+
+    /*
+     * Visibility state
+     */
+    // the layer stack this layer belongs to
+    std::optional<uint32_t> layerStackId;
+
+    // If true, this layer should be only visible on the internal display
+    bool internalOnly{false};
+
+    // If false, this layer should not be considered visible
+    bool isVisible{true};
+
+    // True if the layer is completely opaque
+    bool isOpaque{true};
+
+    // If true, invalidates the entire visible region
+    bool contentDirty{false};
+
+    // The alpha value for this layer
+    float alpha{1.f};
+
+    // Background blur in pixels
+    int backgroundBlurRadius{0};
+
+    // The transform from layer local coordinates to composition coordinates
+    ui::Transform geomLayerTransform;
+
+    // The inverse of the layer transform
+    ui::Transform geomInverseLayerTransform;
+
+    // The hint from the layer producer as to what portion of the layer is
+    // transparent.
+    Region transparentRegionHint;
+
+    // The blend mode for this layer
+    Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID};
+
+    // The bounds of the layer in layer local coordinates
+    FloatRect geomLayerBounds;
+
+    // length of the shadow in screen space
+    float shadowRadius;
 
     /*
      * Geometry state
@@ -48,23 +124,9 @@
     bool geomUsesSourceCrop{false};
     bool geomBufferUsesDisplayInverseTransform{false};
     uint32_t geomBufferTransform{0};
-    ui::Transform geomLayerTransform;
-    ui::Transform geomInverseLayerTransform;
     Rect geomBufferSize;
     Rect geomContentCrop;
     Rect geomCrop;
-    Region geomActiveTransparentRegion;
-    FloatRect geomLayerBounds;
-
-    /*
-     * Presentation
-     */
-
-    // The blend mode for this layer
-    Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID};
-
-    // The alpha value for this layer
-    float alpha{1.f};
 
     /*
      * Extra metadata
@@ -76,6 +138,8 @@
     // The appId for this layer
     int appId{0};
 
+    GenericLayerMetadataMap metadata;
+
     /*
      * Per-frame content
      */
@@ -93,12 +157,16 @@
     sp<NativeHandle> sidebandStream;
 
     // The color for this layer
-    Hwc2::IComposerClient::Color color;
+    half4 color;
 
     /*
      * Per-frame presentation state
      */
 
+    // If true, this layer will use the dataspace chosen for the output and
+    // ignore the dataspace value just below
+    bool isColorspaceAgnostic{false};
+
     // The dataspace for this layer
     ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
 
@@ -107,6 +175,22 @@
 
     // The color transform
     mat4 colorTransform;
+    bool colorTransformIsIdentity{true};
+
+    // True if the layer has protected content
+    bool hasProtectedContent{false};
+
+    /*
+     * Cursor state
+     */
+
+    // The output-independent frame for the cursor
+    Rect cursorFrame;
+
+    virtual ~LayerFECompositionState();
+
+    // Debugging
+    virtual void dump(std::string& out) const;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 54e6bd6..9622e78 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -17,10 +17,16 @@
 #pragma once
 
 #include <cstdint>
+#include <iterator>
 #include <optional>
 #include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
 
-#include <math/mat4.h>
+#include <compositionengine/LayerFE.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/Fence.h>
 #include <ui/GraphicTypes.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -28,14 +34,20 @@
 
 #include "DisplayHardware/DisplayIdentification.h"
 
+namespace HWC2 {
+class Layer;
+} // namespace HWC2
+
 namespace android::compositionengine {
 
 class DisplayColorProfile;
-class Layer;
 class LayerFE;
 class RenderSurface;
 class OutputLayer;
 
+struct CompositionRefreshArgs;
+struct LayerFECompositionState;
+
 namespace impl {
 struct OutputCompositionState;
 } // namespace impl
@@ -45,7 +57,95 @@
  */
 class Output {
 public:
-    using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>;
+    using ReleasedLayers = std::vector<wp<LayerFE>>;
+    using UniqueFELayerStateMap = std::unordered_map<LayerFE*, LayerFECompositionState*>;
+
+    // A helper class for enumerating the output layers using a C++11 ranged-based for loop
+    template <typename T>
+    class OutputLayersEnumerator {
+    public:
+        // TODO(lpique): Consider turning this into a C++20 view when possible.
+        template <bool IsConstIter>
+        class IteratorImpl {
+        public:
+            // Required definitions to be considered an iterator
+            using iterator_category = std::forward_iterator_tag;
+            using value_type = decltype(std::declval<T>().getOutputLayerOrderedByZByIndex(0));
+            using difference_type = std::ptrdiff_t;
+            using pointer = std::conditional_t<IsConstIter, const value_type*, value_type*>;
+            using reference = std::conditional_t<IsConstIter, const value_type&, value_type&>;
+
+            IteratorImpl() = default;
+            IteratorImpl(const T* output, size_t index) : mOutput(output), mIndex(index) {}
+
+            value_type operator*() const {
+                return mOutput->getOutputLayerOrderedByZByIndex(mIndex);
+            }
+            value_type operator->() const {
+                return mOutput->getOutputLayerOrderedByZByIndex(mIndex);
+            }
+
+            bool operator==(const IteratorImpl& other) const {
+                return mOutput == other.mOutput && mIndex == other.mIndex;
+            }
+            bool operator!=(const IteratorImpl& other) const { return !operator==(other); }
+
+            IteratorImpl& operator++() {
+                ++mIndex;
+                return *this;
+            }
+            IteratorImpl operator++(int) {
+                auto prev = *this;
+                ++mIndex;
+                return prev;
+            }
+
+        private:
+            const T* mOutput{nullptr};
+            size_t mIndex{0};
+        };
+
+        using iterator = IteratorImpl<false>;
+        using const_iterator = IteratorImpl<true>;
+
+        explicit OutputLayersEnumerator(const T& output) : mOutput(output) {}
+        auto begin() const { return iterator(&mOutput, 0); }
+        auto end() const { return iterator(&mOutput, mOutput.getOutputLayerCount()); }
+        auto cbegin() const { return const_iterator(&mOutput, 0); }
+        auto cend() const { return const_iterator(&mOutput, mOutput.getOutputLayerCount()); }
+
+    private:
+        const T& mOutput;
+    };
+
+    struct FrameFences {
+        sp<Fence> presentFence{Fence::NO_FENCE};
+        sp<Fence> clientTargetAcquireFence{Fence::NO_FENCE};
+        std::unordered_map<HWC2::Layer*, sp<Fence>> layerFences;
+    };
+
+    struct ColorProfile {
+        ui::ColorMode mode{ui::ColorMode::NATIVE};
+        ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+        ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+        ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN};
+    };
+
+    // Use internally to incrementally compute visibility/coverage
+    struct CoverageState {
+        explicit CoverageState(LayerFESet& latchedLayers) : latchedLayers(latchedLayers) {}
+
+        // The set of layers that had been latched for the coverage calls, to
+        // avoid duplicate requests to obtain the same front-end layer state.
+        LayerFESet& latchedLayers;
+
+        // The region of the output which is covered by layers
+        Region aboveCoveredLayers;
+        // The region of the output which is opaquely covered by layers
+        Region aboveOpaqueLayers;
+        // The region of the output which should be considered dirty
+        Region dirtyRegion;
+    };
 
     virtual ~Output();
 
@@ -54,12 +154,16 @@
     // constructor.
     virtual bool isValid() const = 0;
 
+    // Returns the DisplayId the output represents, if it has one
+    virtual std::optional<DisplayId> getDisplayId() 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;
+    virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
+                               const Rect& viewport, const Rect& sourceClip,
+                               const Rect& destinationClip, bool needsFiltering) = 0;
     // Sets the bounds to use
     virtual void setBounds(const ui::Size&) = 0;
 
@@ -67,11 +171,8 @@
     // 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;
+    virtual void setColorProfile(const ColorProfile&) = 0;
 
     // Outputs a string with a state dump
     virtual void dump(std::string&) const = 0;
@@ -111,28 +212,71 @@
     // 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;
+    virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0;
+
+    // Determines if a layer belongs to the output.
+    virtual bool belongsInOutput(const sp<LayerFE>&) const = 0;
 
     // Returns a pointer to the output layer corresponding to the given layer on
     // this output, or nullptr if the layer does not have one
-    virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0;
+    virtual OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const = 0;
 
-    // Gets the OutputLayer corresponding to the input Layer instance from the
-    // current ordered set of output layers. If there is no such layer, a new
-    // one is created and returned.
-    virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::optional<DisplayId>,
-                                                                std::shared_ptr<Layer>,
-                                                                sp<LayerFE>) = 0;
+    // Immediately clears all layers from the output.
+    virtual void clearOutputLayers() = 0;
 
-    // Sets the new ordered set of output layers for this output
-    virtual void setOutputLayersOrderedByZ(OutputLayers&&) = 0;
+    // For tests use only. Creates and appends an OutputLayer into the output.
+    virtual OutputLayer* injectOutputLayerForTest(const sp<LayerFE>&) = 0;
 
-    // Gets the ordered set of output layers for this output
-    virtual const OutputLayers& getOutputLayersOrderedByZ() const = 0;
+    // Gets the count of output layers managed by this output
+    virtual size_t getOutputLayerCount() const = 0;
+
+    // Gets an output layer in Z order given its index
+    virtual OutputLayer* getOutputLayerOrderedByZByIndex(size_t) const = 0;
+
+    // A helper function for enumerating all the output layers in Z order using
+    // a C++11 range-based for loop.
+    auto getOutputLayersOrderedByZ() const { return OutputLayersEnumerator(*this); }
+
+    // Sets the new set of layers being released this frame
+    virtual void setReleasedLayers(ReleasedLayers&&) = 0;
+
+    // Prepare the output, updating the OutputLayers used in the output
+    virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0;
+
+    // Presents the output, finalizing all composition details
+    virtual void present(const CompositionRefreshArgs&) = 0;
+
+    // Latches the front-end layer state for each output layer
+    virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0;
 
 protected:
     virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0;
     virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0;
+
+    virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0;
+    virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0;
+    virtual void ensureOutputLayerIfVisible(sp<LayerFE>&, CoverageState&) = 0;
+    virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0;
+
+    virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0;
+    virtual void setColorTransform(const CompositionRefreshArgs&) = 0;
+    virtual void updateColorProfile(const CompositionRefreshArgs&) = 0;
+    virtual void beginFrame() = 0;
+    virtual void prepareFrame() = 0;
+    virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0;
+    virtual void finishFrame(const CompositionRefreshArgs&) = 0;
+    virtual std::optional<base::unique_fd> composeSurfaces(const Region&) = 0;
+    virtual void postFramebuffer() = 0;
+    virtual void chooseCompositionStrategy() = 0;
+    virtual bool getSkipColorTransform() const = 0;
+    virtual FrameFences presentAndGetFrameFences() = 0;
+    virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
+            bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
+    virtual void appendRegionFlashRequests(
+            const Region& flashRegion,
+            std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
+    virtual void setExpensiveRenderingExpected(bool enabled) = 0;
+    virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
 };
 
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h
similarity index 71%
rename from services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
rename to services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h
index db3312b..6e798ce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h
@@ -16,20 +16,12 @@
 
 #pragma once
 
-#include <utils/RefBase.h>
-
 namespace android::compositionengine {
 
-class CompositionEngine;
-class LayerFE;
-
-/**
- * A parameter object for creating Layer instances
- */
-struct LayerCreationArgs {
-    // A weak pointer to the front-end layer instance that the new layer will
-    // represent.
-    wp<LayerFE> layerFE;
+enum class OutputColorSetting : int32_t {
+    kManaged = 0,
+    kUnmanaged = 1,
+    kEnhanced = 2,
 };
 
-} // namespace android::compositionengine
+} // namespace android::compositionengine
\ No newline at end of file
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index cd63b57..007b0e8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -21,15 +21,26 @@
 
 #include <utils/StrongPointer.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "DisplayHardware/ComposerHal.h"
 #include "DisplayHardware/DisplayIdentification.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+namespace HWC2 {
+class Layer;
+} // namespace HWC2
+
 namespace android {
 
 namespace compositionengine {
 
 class CompositionEngine;
 class Output;
-class Layer;
 class LayerFE;
 
 namespace impl {
@@ -43,12 +54,12 @@
 public:
     virtual ~OutputLayer();
 
+    // Sets the HWC2::Layer associated with this layer
+    virtual void setHwcLayer(std::shared_ptr<HWC2::Layer>) = 0;
+
     // Gets the output which owns this output layer
     virtual const Output& getOutput() const = 0;
 
-    // Gets the display-independent layer which this output layer represents
-    virtual Layer& getLayer() const = 0;
-
     // Gets the front-end layer interface this output layer represents
     virtual LayerFE& getLayerFE() const = 0;
 
@@ -66,12 +77,36 @@
 
     // Recalculates the state of the output layer from the output-independent
     // layer. If includeGeometry is false, the geometry state can be skipped.
-    virtual void updateCompositionState(bool includeGeometry) = 0;
+    virtual void updateCompositionState(bool includeGeometry, bool forceClientComposition) = 0;
 
     // Writes the geometry state to the HWC, or does nothing if this layer does
     // not use the HWC. If includeGeometry is false, the geometry state can be
     // skipped.
-    virtual void writeStateToHWC(bool includeGeometry) const = 0;
+    virtual void writeStateToHWC(bool includeGeometry) = 0;
+
+    // Updates the cursor position with the HWC
+    virtual void writeCursorPositionToHWC() const = 0;
+
+    // Returns the HWC2::Layer associated with this layer, if it exists
+    virtual HWC2::Layer* getHwcLayer() const = 0;
+
+    // Returns true if the current layer state requires client composition
+    virtual bool requiresClientComposition() const = 0;
+
+    // Returns true if the current layer should be treated as a cursor layer
+    virtual bool isHardwareCursor() const = 0;
+
+    // Applies a HWC device requested composition type change
+    virtual void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) = 0;
+
+    // Prepares to apply any HWC device layer requests
+    virtual void prepareForDeviceLayerRequests() = 0;
+
+    // Applies a HWC device layer request
+    virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0;
+
+    // Returns true if the composition settings scale pixels
+    virtual bool needsFiltering() const = 0;
 
     // Debugging
     virtual void dump(std::string& result) const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
index e21128c..5ce2fdc 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h
@@ -71,22 +71,18 @@
     virtual status_t beginFrame(bool mustRecompose) = 0;
 
     // Prepares the frame for rendering
-    virtual status_t prepareFrame() = 0;
+    virtual void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) = 0;
 
     // Allocates a buffer as scratch space for GPU composition
     virtual sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) = 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;
+    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;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/ClientCompositionRequestCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/ClientCompositionRequestCache.h
new file mode 100644
index 0000000..c14a6e8
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/ClientCompositionRequestCache.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <deque>
+
+#include <compositionengine/LayerFE.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+// The cache is used to skip duplicate client composition requests. We do so by keeping track
+// of every composition request and the buffer that the request is rendered into. During the
+// next composition request, if the request matches what was rendered into the buffer, then
+// we can skip of the request, pass back an empty fence, and let HWC use the previous render
+// result.
+//
+// The cache is a mapping of the RenderSurface buffer id (unique per process) and a snapshot of
+// the composition request. We need to make sure the request, including the order of the
+// layers, do not change from call to call. The snapshot removes strong references to the
+// client buffer id so we don't extend the lifetime of the buffer by storing it in the cache.
+class ClientCompositionRequestCache {
+public:
+    explicit ClientCompositionRequestCache(uint32_t cacheSize) : mMaxCacheSize(cacheSize){};
+    ~ClientCompositionRequestCache() = default;
+    bool exists(uint64_t bufferId, const renderengine::DisplaySettings& display,
+                const std::vector<LayerFE::LayerSettings>& layerSettings) const;
+    void add(uint64_t bufferId, const renderengine::DisplaySettings& display,
+             const std::vector<LayerFE::LayerSettings>& layerSettings);
+    void remove(uint64_t bufferId);
+
+private:
+    uint32_t mMaxCacheSize;
+    struct ClientCompositionRequest {
+        renderengine::DisplaySettings display;
+        std::vector<LayerFE::LayerSettings> layerSettings;
+        ClientCompositionRequest(const renderengine::DisplaySettings& _display,
+                                 const std::vector<LayerFE::LayerSettings>& _layerSettings);
+        bool equals(const renderengine::DisplaySettings& _display,
+                    const std::vector<LayerFE::LayerSettings>& _layerSettings) const;
+    };
+
+    // Cache of requests, keyed by corresponding GraphicBuffer ID.
+    std::deque<std::pair<uint64_t /* bufferId */, ClientCompositionRequest>> mCache;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index b01eb64..386808d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -26,9 +26,9 @@
     ~CompositionEngine() override;
 
     std::shared_ptr<compositionengine::Display> createDisplay(
-            compositionengine::DisplayCreationArgs&&) override;
-    std::shared_ptr<compositionengine::Layer> createLayer(
-            compositionengine::LayerCreationArgs&&) override;
+            const compositionengine::DisplayCreationArgs&) override;
+    std::unique_ptr<compositionengine::LayerFECompositionState> createLayerFECompositionState()
+            override;
 
     HWComposer& getHwComposer() const override;
     void setHwComposer(std::unique_ptr<HWComposer>) override;
@@ -36,9 +36,32 @@
     renderengine::RenderEngine& getRenderEngine() const override;
     void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
 
+    TimeStats& getTimeStats() const override;
+    void setTimeStats(const std::shared_ptr<TimeStats>&) override;
+
+    bool needsAnotherUpdate() const override;
+    nsecs_t getLastFrameRefreshTimestamp() const override;
+
+    void present(CompositionRefreshArgs&) override;
+
+    void updateCursorAsync(CompositionRefreshArgs&) override;
+
+    void preComposition(CompositionRefreshArgs&) override;
+
+    // Debugging
+    void dump(std::string&) const override;
+
+    void updateLayerStateFromFE(CompositionRefreshArgs& args);
+
+    // Testing
+    void setNeedsAnotherUpdateForTest(bool);
+
 private:
     std::unique_ptr<HWComposer> mHwComposer;
     std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+    std::shared_ptr<TimeStats> mTimeStats;
+    bool mNeedsAnotherUpdate = false;
+    nsecs_t mRefreshStartTime = 0;
 };
 
 std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 0e20c43..fb597ce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -16,45 +16,83 @@
 
 #pragma once
 
-#include <memory>
-
 #include <compositionengine/Display.h>
+#include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/impl/Output.h>
 
+#include <memory>
+
 #include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/HWComposer.h"
+#include "DisplayHardware/PowerAdvisor.h"
 
 namespace android::compositionengine {
 
 class CompositionEngine;
 
-struct DisplayCreationArgs;
-
 namespace impl {
 
-class Display : public compositionengine::impl::Output, public compositionengine::Display {
+// The implementation class contains the common implementation, but does not
+// actually contain the final display state.
+class Display : public compositionengine::impl::Output, public virtual compositionengine::Display {
 public:
-    Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+    explicit Display(const compositionengine::DisplayCreationArgs&);
     virtual ~Display();
 
     // compositionengine::Output overrides
+    std::optional<DisplayId> getDisplayId() const override;
     void dump(std::string&) const override;
-    void setColorTransform(const mat4&) override;
-    void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+    using compositionengine::impl::Output::setReleasedLayers;
+    void setReleasedLayers(const CompositionRefreshArgs&) override;
+    void setColorTransform(const CompositionRefreshArgs&) override;
+    void setColorProfile(const ColorProfile&) override;
+    void chooseCompositionStrategy() override;
+    bool getSkipColorTransform() const override;
+    compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+    void setExpensiveRenderingExpected(bool) override;
+    void finishFrame(const CompositionRefreshArgs&) 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;
+    void createDisplayColorProfile(
+            const compositionengine::DisplayColorProfileCreationArgs&) override;
+    void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override;
+    void createClientCompositionCache(uint32_t cacheSize) override;
+
+    // Internal helpers used by chooseCompositionStrategy()
+    using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes;
+    using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
+    using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
+    virtual bool anyLayersRequireClientComposition() const;
+    virtual bool allLayersRequireClientComposition() const;
+    virtual void applyChangedTypesToLayers(const ChangedTypes&);
+    virtual void applyDisplayRequests(const DisplayRequests&);
+    virtual void applyLayerRequestsToLayers(const LayerRequests&);
+
+    // Internal
+    std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
 
 private:
     const bool mIsVirtual;
     std::optional<DisplayId> mId;
+    Hwc2::PowerAdvisor* const mPowerAdvisor{nullptr};
 };
 
-std::shared_ptr<compositionengine::Display> createDisplay(
-        const compositionengine::CompositionEngine&, compositionengine::DisplayCreationArgs&&);
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseDisplay, typename CompositionEngine, typename DisplayCreationArgs>
+std::shared_ptr<BaseDisplay> createDisplayTemplated(const CompositionEngine& compositionEngine,
+                                                    const DisplayCreationArgs& args) {
+    return createOutputTemplated<BaseDisplay>(compositionEngine, args);
+}
+
+std::shared_ptr<Display> createDisplay(const compositionengine::CompositionEngine&,
+                                       const 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
index 49c2d2c..9bc0e68 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h
@@ -33,7 +33,7 @@
 
 class DisplayColorProfile : public compositionengine::DisplayColorProfile {
 public:
-    DisplayColorProfile(DisplayColorProfileCreationArgs&&);
+    DisplayColorProfile(const DisplayColorProfileCreationArgs&);
     ~DisplayColorProfile() override;
 
     bool isValid() const override;
@@ -54,6 +54,8 @@
     bool hasDolbyVisionSupport() const override;
 
     const HdrCapabilities& getHdrCapabilities() const override;
+    bool isDataspaceSupported(ui::Dataspace) const override;
+    ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace) const override;
 
     void dump(std::string&) const override;
 
@@ -89,7 +91,7 @@
 };
 
 std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
-        DisplayColorProfileCreationArgs&&);
+        const DisplayColorProfileCreationArgs&);
 
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
index 8eec035..2864c10 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h
@@ -19,7 +19,15 @@
 #include <cstdint>
 #include <vector>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gui/BufferQueue.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <utils/StrongPointer.h>
 
 namespace android {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
deleted file mode 100644
index 3e56b21..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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/Layer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <utils/RefBase.h>
-#include <utils/StrongPointer.h>
-
-namespace android::compositionengine {
-
-class CompositionEngine;
-class LayerFE;
-
-struct LayerCreationArgs;
-
-namespace impl {
-
-class Display;
-
-class Layer : public compositionengine::Layer {
-public:
-    Layer(const CompositionEngine&, compositionengine::LayerCreationArgs&&);
-    ~Layer() override;
-
-    sp<LayerFE> getLayerFE() const override;
-
-    const LayerCompositionState& getState() const override;
-    LayerCompositionState& editState() override;
-
-    void dump(std::string& result) const override;
-
-private:
-    const compositionengine::CompositionEngine& mCompositionEngine;
-    const wp<LayerFE> mLayerFE;
-
-    LayerCompositionState mState;
-};
-
-std::shared_ptr<compositionengine::Layer> createLayer(const compositionengine::CompositionEngine&,
-                                                      compositionengine::LayerCreationArgs&&);
-
-} // namespace impl
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index b1d1f42..d41337c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -16,36 +16,36 @@
 
 #pragma once
 
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
 #include <memory>
 #include <utility>
 #include <vector>
 
-#include <compositionengine/Output.h>
-#include <compositionengine/impl/OutputCompositionState.h>
+namespace android::compositionengine::impl {
 
-namespace android::compositionengine {
-
-class CompositionEngine;
-class Layer;
-class OutputLayer;
-
-namespace impl {
-
+// The implementation class contains the common implementation, but does not
+// actually contain the final output state.
 class Output : public virtual compositionengine::Output {
 public:
-    Output(const CompositionEngine&);
     ~Output() override;
 
+    // compositionengine::Output overrides
     bool isValid() const override;
-
+    std::optional<DisplayId> getDisplayId() 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 setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame,
+                       const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip,
+                       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 setColorTransform(const compositionengine::CompositionRefreshArgs&) override;
+    void setColorProfile(const ColorProfile&) override;
 
     void dump(std::string&) const override;
 
@@ -58,42 +58,174 @@
     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;
+    bool belongsInOutput(std::optional<uint32_t>, bool) const override;
+    bool belongsInOutput(const sp<LayerFE>&) const override;
 
-    compositionengine::OutputLayer* getOutputLayerForLayer(
-            compositionengine::Layer*) const override;
-    std::unique_ptr<compositionengine::OutputLayer> getOrCreateOutputLayer(
-            std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>,
-            sp<LayerFE>) override;
-    void setOutputLayersOrderedByZ(OutputLayers&&) override;
-    const OutputLayers& getOutputLayersOrderedByZ() const override;
+    compositionengine::OutputLayer* getOutputLayerForLayer(const sp<LayerFE>&) const override;
+
+    void setReleasedLayers(ReleasedLayers&&) override;
+
+    void prepare(const CompositionRefreshArgs&, LayerFESet&) override;
+    void present(const CompositionRefreshArgs&) override;
+
+    void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override;
+    void collectVisibleLayers(const CompositionRefreshArgs&,
+                              compositionengine::Output::CoverageState&) override;
+    void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&,
+                                    compositionengine::Output::CoverageState&) override;
+    void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override;
+
+    void updateLayerStateFromFE(const CompositionRefreshArgs&) const override;
+    void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override;
+    void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
+    void beginFrame() override;
+    void prepareFrame() override;
+    void devOptRepaintFlash(const CompositionRefreshArgs&) override;
+    void finishFrame(const CompositionRefreshArgs&) override;
+    std::optional<base::unique_fd> composeSurfaces(const Region&) override;
+    void postFramebuffer() override;
+    void cacheClientCompositionRequests(uint32_t) override;
 
     // Testing
+    const ReleasedLayers& getReleasedLayersForTest() const;
     void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>);
     void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>);
 
 protected:
-    const CompositionEngine& getCompositionEngine() const;
+    std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
+    std::optional<size_t> findCurrentOutputLayerForLayer(
+            const sp<compositionengine::LayerFE>&) const;
+    void chooseCompositionStrategy() override;
+    bool getSkipColorTransform() const override;
+    compositionengine::Output::FrameFences presentAndGetFrameFences() override;
+    std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
+            bool supportsProtectedContent, Region& clearRegion,
+            ui::Dataspace outputDataspace) override;
+    void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
+    void setExpensiveRenderingExpected(bool enabled) override;
     void dumpBase(std::string&) const;
 
+    // Implemented by the final implementation for the final state it uses.
+    virtual compositionengine::OutputLayer* ensureOutputLayer(std::optional<size_t>,
+                                                              const sp<LayerFE>&) = 0;
+    virtual compositionengine::OutputLayer* injectOutputLayerForTest(const sp<LayerFE>&) = 0;
+    virtual void finalizePendingOutputLayers() = 0;
+    virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0;
+    virtual void dumpState(std::string& out) const = 0;
+
 private:
     void dirtyEntireOutput();
-
-    const CompositionEngine& mCompositionEngine;
+    compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
+    ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
+    compositionengine::Output::ColorProfile pickColorProfile(
+            const compositionengine::CompositionRefreshArgs&) const;
 
     std::string mName;
 
-    OutputCompositionState mState;
-
     std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile;
     std::unique_ptr<compositionengine::RenderSurface> mRenderSurface;
 
-    OutputLayers mOutputLayersOrderedByZ;
+    ReleasedLayers mReleasedLayers;
+    OutputLayer* mLayerRequestingBackgroundBlur = nullptr;
+    std::unique_ptr<ClientCompositionRequestCache> mClientCompositionRequestCache;
 };
 
-} // namespace impl
-} // namespace android::compositionengine
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseOutput, typename CompositionEngine, typename... Args>
+std::shared_ptr<BaseOutput> createOutputTemplated(const CompositionEngine& compositionEngine,
+                                                  Args... args) {
+    class Output final : public BaseOutput {
+    public:
+// Clang incorrectly complains that these are unused.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
+
+        using OutputCompositionState = std::remove_const_t<
+                std::remove_reference_t<decltype(std::declval<BaseOutput>().getState())>>;
+        using OutputLayer = std::remove_pointer_t<decltype(
+                std::declval<BaseOutput>().getOutputLayerOrderedByZByIndex(0))>;
+
+#pragma clang diagnostic pop
+
+        explicit Output(const CompositionEngine& compositionEngine, Args... args)
+              : BaseOutput(std::forward<Args>(args)...), mCompositionEngine(compositionEngine) {}
+        ~Output() override = default;
+
+    private:
+        // compositionengine::Output overrides
+        const OutputCompositionState& getState() const override { return mState; }
+
+        OutputCompositionState& editState() override { return mState; }
+
+        size_t getOutputLayerCount() const override {
+            return mCurrentOutputLayersOrderedByZ.size();
+        }
+
+        OutputLayer* getOutputLayerOrderedByZByIndex(size_t index) const override {
+            if (index >= mCurrentOutputLayersOrderedByZ.size()) {
+                return nullptr;
+            }
+            return mCurrentOutputLayersOrderedByZ[index].get();
+        }
+
+        // compositionengine::impl::Output overrides
+        const CompositionEngine& getCompositionEngine() const override {
+            return mCompositionEngine;
+        };
+
+        OutputLayer* ensureOutputLayer(std::optional<size_t> prevIndex,
+                                       const sp<LayerFE>& layerFE) {
+            auto outputLayer = (prevIndex && *prevIndex <= mCurrentOutputLayersOrderedByZ.size())
+                    ? std::move(mCurrentOutputLayersOrderedByZ[*prevIndex])
+                    : BaseOutput::createOutputLayer(layerFE);
+            auto result = outputLayer.get();
+            mPendingOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+            return result;
+        }
+
+        void finalizePendingOutputLayers() override {
+            // The pending layers are added in reverse order. Reverse them to
+            // get the back-to-front ordered list of layers.
+            std::reverse(mPendingOutputLayersOrderedByZ.begin(),
+                         mPendingOutputLayersOrderedByZ.end());
+
+            mCurrentOutputLayersOrderedByZ = std::move(mPendingOutputLayersOrderedByZ);
+        }
+
+        void dumpState(std::string& out) const override { mState.dump(out); }
+
+        OutputLayer* injectOutputLayerForTest(const sp<LayerFE>& layerFE) override {
+            auto outputLayer = BaseOutput::createOutputLayer(layerFE);
+            auto result = outputLayer.get();
+            mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+            return result;
+        }
+
+        // Note: This is declared as a private virtual non-override so it can be
+        // an override implementation in the unit tests, but otherwise is not an
+        // accessible override for the normal implementation.
+        virtual void injectOutputLayerForTest(std::unique_ptr<OutputLayer> outputLayer) {
+            mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));
+        }
+
+        void clearOutputLayers() override {
+            mCurrentOutputLayersOrderedByZ.clear();
+            mPendingOutputLayersOrderedByZ.clear();
+        }
+
+        const CompositionEngine& mCompositionEngine;
+        OutputCompositionState mState;
+        std::vector<std::unique_ptr<OutputLayer>> mCurrentOutputLayersOrderedByZ;
+        std::vector<std::unique_ptr<OutputLayer>> mPendingOutputLayersOrderedByZ;
+    };
+
+    return std::make_shared<Output>(compositionEngine, std::forward<Args>(args)...);
+}
+
+std::shared_ptr<Output> createOutput(const compositionengine::CompositionEngine&);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 0c47eb5..66ed2b6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -19,7 +19,16 @@
 #include <cstdint>
 
 #include <math/mat4.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <ui/GraphicTypes.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Transform.h>
@@ -35,6 +44,19 @@
     // If false, this output is not considered secure
     bool isSecure{false};
 
+    // If true, the current frame on this output uses client composition
+    bool usesClientComposition{false};
+
+    // If true, the current frame on this output uses device composition
+    bool usesDeviceComposition{false};
+
+    // If true, the client target should be flipped when performing client
+    // composition
+    bool flipClientTarget{false};
+
+    // If true, the current frame reused the buffer from a previous client composition
+    bool reusedClientComposition{false};
+
     // If true, this output displays layers that are internal-only
     bool layerStackInternal{false};
 
@@ -57,8 +79,11 @@
     // The logical space user viewport rectangle
     Rect viewport;
 
-    // The physical space scissor rectangle
-    Rect scissor;
+    // The physical space source clip rectangle
+    Rect sourceClip;
+
+    // The physical space destination clip rectangle
+    Rect destinationClip;
 
     // If true, RenderEngine filtering should be enabled
     bool needsFiltering{false};
@@ -76,11 +101,8 @@
     // 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};
-
-    // The color transform matrix to apply, corresponding with colorTransform.
-    mat4 colorTransformMat;
+    // The color transform matrix to apply
+    mat4 colorTransformMatrix;
 
     // Current active color mode
     ui::ColorMode colorMode{ui::ColorMode::NATIVE};
@@ -88,9 +110,12 @@
     // Current active render intent
     ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
 
-    // Current active dstaspace
+    // Current active dataspace
     ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
 
+    // Current target dataspace
+    ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN};
+
     // Debugging
     void dump(std::string& result) const;
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 6a4818f..79df9b2 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -20,50 +20,107 @@
 #include <string>
 
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <ui/FloatRect.h>
 #include <ui/Rect.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
 
-namespace android::compositionengine::impl {
+namespace android::compositionengine {
 
-class OutputLayer : public compositionengine::OutputLayer {
+struct LayerFECompositionState;
+
+namespace impl {
+
+// The implementation class contains the common implementation, but does not
+// actually contain the final layer state.
+class OutputLayer : public virtual compositionengine::OutputLayer {
 public:
-    OutputLayer(const compositionengine::Output&, std::shared_ptr<compositionengine::Layer>,
-                sp<compositionengine::LayerFE>);
     ~OutputLayer() override;
 
-    void initialize(const CompositionEngine&, std::optional<DisplayId>);
+    void setHwcLayer(std::shared_ptr<HWC2::Layer>) override;
 
-    const compositionengine::Output& getOutput() const override;
-    compositionengine::Layer& getLayer() const override;
-    compositionengine::LayerFE& getLayerFE() const override;
+    void updateCompositionState(bool includeGeometry, bool forceClientComposition) override;
+    void writeStateToHWC(bool) override;
+    void writeCursorPositionToHWC() const override;
 
-    const OutputLayerCompositionState& getState() const override;
-    OutputLayerCompositionState& editState() override;
+    HWC2::Layer* getHwcLayer() const override;
+    bool requiresClientComposition() const override;
+    bool isHardwareCursor() const override;
+    void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) override;
+    void prepareForDeviceLayerRequests() override;
+    void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
+    bool needsFiltering() const override;
 
-    void updateCompositionState(bool) override;
-    void writeStateToHWC(bool) const override;
-
-    void dump(std::string& result) const override;
+    void dump(std::string&) const override;
 
     virtual FloatRect calculateOutputSourceCrop() const;
     virtual Rect calculateOutputDisplayFrame() const;
     virtual uint32_t calculateOutputRelativeBufferTransform() const;
 
+protected:
+    // Implemented by the final implementation for the final state it uses.
+    virtual void dumpState(std::string&) const = 0;
+
 private:
     Rect calculateInitialCrop() const;
-
-    const compositionengine::Output& mOutput;
-    std::shared_ptr<compositionengine::Layer> mLayer;
-    sp<compositionengine::LayerFE> mLayerFE;
-
-    OutputLayerCompositionState mState;
+    void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+    void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
+    void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
+    void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition);
+    void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
+                                               Hwc2::IComposerClient::Composition to) const;
 };
 
-std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
-        const CompositionEngine&, std::optional<DisplayId>, const compositionengine::Output&,
-        std::shared_ptr<compositionengine::Layer>, sp<compositionengine::LayerFE>);
+// This template factory function standardizes the implementation details of the
+// final class using the types actually required by the implementation. This is
+// not possible to do in the base class as those types may not even be visible
+// to the base code.
+template <typename BaseOutputLayer>
+std::unique_ptr<BaseOutputLayer> createOutputLayerTemplated(const Output& output,
+                                                            sp<LayerFE> layerFE) {
+    class OutputLayer final : public BaseOutputLayer {
+    public:
+// Clang incorrectly complains that these are unused.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-local-typedef"
 
-} // namespace android::compositionengine::impl
+        using OutputLayerCompositionState = std::remove_const_t<
+                std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getState())>>;
+        using Output = std::remove_const_t<
+                std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getOutput())>>;
+        using LayerFE =
+                std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayerFE())>;
+
+#pragma clang diagnostic pop
+
+        OutputLayer(const Output& output, const sp<LayerFE>& layerFE)
+              : mOutput(output), mLayerFE(layerFE) {}
+        ~OutputLayer() override = default;
+
+    private:
+        // compositionengine::OutputLayer overrides
+        const Output& getOutput() const override { return mOutput; }
+        LayerFE& getLayerFE() const override { return *mLayerFE; }
+        const OutputLayerCompositionState& getState() const override { return mState; }
+        OutputLayerCompositionState& editState() override { return mState; }
+
+        // compositionengine::impl::OutputLayer overrides
+        void dumpState(std::string& out) const override { mState.dump(out); }
+
+        const Output& mOutput;
+        const sp<LayerFE> mLayerFE;
+        OutputLayerCompositionState mState;
+    };
+
+    return std::make_unique<OutputLayer>(output, layerFE);
+}
+
+std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output&,
+                                               const sp<LayerFE>&);
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index b78e9e0..b0a9bc9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -23,11 +23,19 @@
 #include <compositionengine/impl/HwcBufferCache.h>
 #include <renderengine/Mesh.h>
 #include <ui/FloatRect.h>
+#include <ui/GraphicTypes.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/ComposerHal.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace HWC2 {
 class Layer;
 } // namespace HWC2
@@ -39,9 +47,21 @@
 namespace compositionengine::impl {
 
 struct OutputLayerCompositionState {
-    // The region of this layer which is visible on this output
+    // The portion of the layer that is not obscured by opaque layers on top
     Region visibleRegion;
 
+    // The portion of the layer that is not obscured and is also opaque
+    Region visibleNonTransparentRegion;
+
+    // The portion of the layer that is obscured by opaque layers on top
+    Region coveredRegion;
+
+    // The visibleRegion transformed to output space
+    Region outputSpaceVisibleRegion;
+
+    // Region cast by the layer's shadow
+    Region shadowRegion;
+
     // If true, client composition will be used on this output
     bool forceClientComposition{false};
 
@@ -57,8 +77,11 @@
     // The buffer transform to use for this layer o on this output.
     Hwc2::Transform bufferTransform{static_cast<Hwc2::Transform>(0)};
 
+    // The dataspace for this layer
+    ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
     // The Z order index of this layer on this output
-    uint32_t z;
+    uint32_t z{0};
 
     /*
      * HWC state
@@ -70,7 +93,7 @@
         // The HWC Layer backing this layer
         std::shared_ptr<HWC2::Layer> hwcLayer;
 
-        // The HWC composition type for this layer
+        // The most recently set HWC composition type for this layer
         Hwc2::IComposerClient::Composition hwcCompositionType{
                 Hwc2::IComposerClient::Composition::INVALID};
 
@@ -85,6 +108,9 @@
 
     // Debugging
     void dump(std::string& result) const;
+
+    // Timestamp for when the layer is queued for client composition
+    nsecs_t clientCompositionTimestamp;
 };
 
 } // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
index 0f57315..692d78d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h
@@ -39,7 +39,7 @@
 class RenderSurface : public compositionengine::RenderSurface {
 public:
     RenderSurface(const CompositionEngine&, compositionengine::Display&,
-                  compositionengine::RenderSurfaceCreationArgs&&);
+                  const compositionengine::RenderSurfaceCreationArgs&);
     ~RenderSurface() override;
 
     bool isValid() const override;
@@ -52,11 +52,10 @@
     void setDisplaySize(const ui::Size&) override;
     void setProtected(bool useProtected) override;
     status_t beginFrame(bool mustRecompose) override;
-    status_t prepareFrame() override;
+    void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override;
     sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override;
-    void queueBuffer(base::unique_fd&& readyFence) override;
+    void queueBuffer(base::unique_fd readyFence) override;
     void onPresentDisplayCompleted() override;
-    void setViewportAndProjection() override;
     void flip() override;
 
     // Debugging
@@ -85,7 +84,7 @@
 
 std::unique_ptr<compositionengine::RenderSurface> createRenderSurface(
         const compositionengine::CompositionEngine&, compositionengine::Display&,
-        compositionengine::RenderSurfaceCreationArgs&&);
+        const compositionengine::RenderSurfaceCreationArgs&);
 
 } // namespace impl
 } // namespace compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 0f57685..f953d0b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -17,8 +17,9 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
-#include <compositionengine/LayerCreationArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <gmock/gmock.h>
 #include <renderengine/RenderEngine.h>
 
@@ -31,14 +32,28 @@
     CompositionEngine();
     ~CompositionEngine() override;
 
-    MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&));
-    MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(LayerCreationArgs&&));
+    MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(const DisplayCreationArgs&));
+    MOCK_METHOD0(createLayerFECompositionState,
+                 std::unique_ptr<compositionengine::LayerFECompositionState>());
 
     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>));
+
+    MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
+    MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
+
+    MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
+    MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t());
+
+    MOCK_METHOD1(present, void(CompositionRefreshArgs&));
+    MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&));
+
+    MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+
+    MOCK_CONST_METHOD1(dump, void(std::string&));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index d763aa6..3a4c70f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -38,8 +38,9 @@
 
     MOCK_METHOD0(disconnect, void());
 
-    MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&));
-    MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&));
+    MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&));
+    MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&));
+    MOCK_METHOD1(createClientCompositionCache, void(uint32_t));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
index 8056c9d..1aaebea 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h
@@ -42,6 +42,9 @@
     MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool());
 
     MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&());
+    MOCK_CONST_METHOD1(isDataspaceSupported, bool(ui::Dataspace));
+    MOCK_CONST_METHOD3(getTargetDataspace,
+                       ui::Dataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace));
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
deleted file mode 100644
index cce3b97..0000000
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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/Layer.h>
-#include <compositionengine/LayerFE.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-#include <gmock/gmock.h>
-
-namespace android::compositionengine::mock {
-
-class Layer : public compositionengine::Layer {
-public:
-    Layer();
-    virtual ~Layer();
-
-    MOCK_CONST_METHOD0(getLayerFE, sp<LayerFE>());
-
-    MOCK_CONST_METHOD0(getState, const CompositionState&());
-    MOCK_METHOD0(editState, CompositionState&());
-
-    MOCK_CONST_METHOD1(dump, void(std::string&));
-};
-
-} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index aab18db..45891a7 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -30,7 +30,15 @@
     LayerFE();
     virtual ~LayerFE();
 
-    MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool));
+    MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
+
+    MOCK_METHOD1(onPreComposition, bool(nsecs_t));
+
+    MOCK_METHOD1(prepareCompositionState, void(compositionengine::LayerFE::StateSubset));
+    MOCK_METHOD1(prepareClientCompositionList,
+                 std::vector<compositionengine::LayerFE::LayerSettings>(
+                         compositionengine::LayerFE::ClientCompositionTargetSettings&));
+
     MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
 
     MOCK_CONST_METHOD0(getDebugName, const char*());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index d0e7b19..346c2d1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -16,8 +16,8 @@
 
 #pragma once
 
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
@@ -33,40 +33,83 @@
     virtual ~Output();
 
     MOCK_CONST_METHOD0(isValid, bool());
+    MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>());
 
     MOCK_METHOD1(setCompositionEnabled, void(bool));
-    MOCK_METHOD6(setProjection,
-                 void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+    MOCK_METHOD7(setProjection,
+                 void(const ui::Transform&, uint32_t, const Rect&, 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_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+    MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
 
     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(getDisplayColorProfile, compositionengine::DisplayColorProfile*());
+    MOCK_METHOD1(setDisplayColorProfile,
+                 void(std::unique_ptr<compositionengine::DisplayColorProfile>));
 
-    MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*());
-    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>));
+    MOCK_CONST_METHOD0(getRenderSurface, compositionengine::RenderSurface*());
+    MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<compositionengine::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));
+    MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool));
+    MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
 
     MOCK_CONST_METHOD1(getOutputLayerForLayer,
-                       compositionengine::OutputLayer*(compositionengine::Layer*));
-    MOCK_METHOD3(getOrCreateOutputLayer,
-                 std::unique_ptr<compositionengine::OutputLayer>(
-                         std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>,
-                         sp<compositionengine::LayerFE>));
-    MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&));
-    MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&());
+                       compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
+    MOCK_METHOD0(clearOutputLayers, void());
+    MOCK_METHOD1(injectOutputLayerForTest,
+                 compositionengine::OutputLayer*(const sp<compositionengine::LayerFE>&));
+    MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+    MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
+
+    MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&));
+
+    MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
+    MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD2(rebuildLayerStacks,
+                 void(const compositionengine::CompositionRefreshArgs&, LayerFESet&));
+    MOCK_METHOD2(collectVisibleLayers,
+                 void(const compositionengine::CompositionRefreshArgs&,
+                      compositionengine::Output::CoverageState&));
+    MOCK_METHOD2(ensureOutputLayerIfVisible,
+                 void(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&));
+    MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&));
+    MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&));
+    MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD0(beginFrame, void());
+
+    MOCK_METHOD0(prepareFrame, void());
+    MOCK_METHOD0(chooseCompositionStrategy, void());
+
+    MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+
+    MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+    MOCK_CONST_METHOD0(getSkipColorTransform, bool());
+
+    MOCK_METHOD0(postFramebuffer, void());
+    MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+
+    MOCK_METHOD3(generateClientCompositionRequests,
+                 std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+    MOCK_METHOD2(appendRegionFlashRequests,
+                 void(const Region&, std::vector<LayerFE::LayerSettings>&));
+    MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+    MOCK_METHOD1(cacheClientCompositionRequests, void(uint32_t));
 };
 
 } // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 29cd08a..2ecbad8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Layer.h>
 #include <compositionengine/LayerFE.h>
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
@@ -31,15 +30,25 @@
     OutputLayer();
     virtual ~OutputLayer();
 
+    MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>));
+
     MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&());
-    MOCK_CONST_METHOD0(getLayer, compositionengine::Layer&());
     MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&());
 
     MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&());
     MOCK_METHOD0(editState, impl::OutputLayerCompositionState&());
 
-    MOCK_METHOD1(updateCompositionState, void(bool));
-    MOCK_CONST_METHOD1(writeStateToHWC, void(bool));
+    MOCK_METHOD2(updateCompositionState, void(bool, bool));
+    MOCK_METHOD1(writeStateToHWC, void(bool));
+    MOCK_CONST_METHOD0(writeCursorPositionToHWC, void());
+
+    MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
+    MOCK_CONST_METHOD0(requiresClientComposition, bool());
+    MOCK_CONST_METHOD0(isHardwareCursor, bool());
+    MOCK_METHOD1(applyDeviceCompositionTypeChange, void(Hwc2::IComposerClient::Composition));
+    MOCK_METHOD0(prepareForDeviceLayerRequests, void());
+    MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
+    MOCK_CONST_METHOD0(needsFiltering, bool());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
 };
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
index ca2299a..ed4d492 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h
@@ -37,11 +37,10 @@
     MOCK_METHOD1(setProtected, void(bool));
     MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace));
     MOCK_METHOD1(beginFrame, status_t(bool mustRecompose));
-    MOCK_METHOD0(prepareFrame, status_t());
+    MOCK_METHOD2(prepareFrame, void(bool, bool));
     MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*));
-    MOCK_METHOD1(queueBuffer, void(base::unique_fd&&));
+    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());
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
new file mode 100644
index 0000000..acaaf4e
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <algorithm>
+
+#include <compositionengine/impl/ClientCompositionRequestCache.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/LayerSettings.h>
+
+namespace android::compositionengine::impl {
+
+namespace {
+LayerFE::LayerSettings getLayerSettingsSnapshot(const LayerFE::LayerSettings& settings) {
+    LayerFE::LayerSettings snapshot = settings;
+    snapshot.source.buffer.buffer = nullptr;
+    snapshot.source.buffer.fence = nullptr;
+    return snapshot;
+}
+
+inline bool equalIgnoringSource(const renderengine::LayerSettings& lhs,
+                                const renderengine::LayerSettings& rhs) {
+    return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
+            lhs.sourceDataspace == rhs.sourceDataspace &&
+            lhs.colorTransform == rhs.colorTransform &&
+            lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+}
+
+inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
+    return lhs.textureName == rhs.textureName &&
+            lhs.useTextureFiltering == rhs.useTextureFiltering &&
+            lhs.textureTransform == rhs.textureTransform &&
+            lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+            lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+            lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+            lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+inline bool equalIgnoringBuffer(const renderengine::LayerSettings& lhs,
+                                const renderengine::LayerSettings& rhs) {
+    // compare LayerSettings without LayerSettings.PixelSource
+    return equalIgnoringSource(lhs, rhs) &&
+
+            // compare LayerSettings.PixelSource without buffer
+            lhs.source.solidColor == rhs.source.solidColor &&
+
+            // compare LayerSettings.PixelSource.Buffer without buffer & fence
+            equalIgnoringBuffer(lhs.source.buffer, rhs.source.buffer);
+}
+
+bool layerSettingsAreEqual(const LayerFE::LayerSettings& lhs, const LayerFE::LayerSettings& rhs) {
+    return lhs.bufferId == rhs.bufferId && lhs.frameNumber == rhs.frameNumber &&
+            equalIgnoringBuffer(lhs, rhs);
+}
+
+} // namespace
+
+ClientCompositionRequestCache::ClientCompositionRequest::ClientCompositionRequest(
+        const renderengine::DisplaySettings& initDisplay,
+        const std::vector<LayerFE::LayerSettings>& initLayerSettings)
+      : display(initDisplay) {
+    layerSettings.reserve(initLayerSettings.size());
+    for (const LayerFE::LayerSettings& settings : initLayerSettings) {
+        layerSettings.push_back(getLayerSettingsSnapshot(settings));
+    }
+}
+
+bool ClientCompositionRequestCache::ClientCompositionRequest::equals(
+        const renderengine::DisplaySettings& newDisplay,
+        const std::vector<LayerFE::LayerSettings>& newLayerSettings) const {
+    return newDisplay == display &&
+            std::equal(layerSettings.begin(), layerSettings.end(), newLayerSettings.begin(),
+                       newLayerSettings.end(), layerSettingsAreEqual);
+}
+
+bool ClientCompositionRequestCache::exists(
+        uint64_t bufferId, const renderengine::DisplaySettings& display,
+        const std::vector<LayerFE::LayerSettings>& layerSettings) const {
+    for (const auto& [cachedBufferId, cachedRequest] : mCache) {
+        if (cachedBufferId == bufferId) {
+            return cachedRequest.equals(display, layerSettings);
+        }
+    }
+    return false;
+}
+
+void ClientCompositionRequestCache::add(uint64_t bufferId,
+                                        const renderengine::DisplaySettings& display,
+                                        const std::vector<LayerFE::LayerSettings>& layerSettings) {
+    const ClientCompositionRequest request(display, layerSettings);
+    for (auto& [cachedBufferId, cachedRequest] : mCache) {
+        if (cachedBufferId == bufferId) {
+            cachedRequest = std::move(request);
+            return;
+        }
+    }
+
+    if (mCache.size() >= mMaxCacheSize) {
+        mCache.pop_front();
+    }
+
+    mCache.emplace_back(bufferId, std::move(request));
+}
+
+void ClientCompositionRequestCache::remove(uint64_t bufferId) {
+    for (auto it = mCache.begin(); it != mCache.end(); it++) {
+        if (it->first == bufferId) {
+            mCache.erase(it);
+            return;
+        }
+    }
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index cb08b81..6203dc6 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -14,13 +14,25 @@
  * limitations under the License.
  */
 
+#include <compositionengine/CompositionRefreshArgs.h>
+#include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/CompositionEngine.h>
 #include <compositionengine/impl/Display.h>
-#include <compositionengine/impl/Layer.h>
+
 #include <renderengine/RenderEngine.h>
+#include <utils/Trace.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine {
 
 CompositionEngine::~CompositionEngine() = default;
@@ -35,12 +47,13 @@
 CompositionEngine::~CompositionEngine() = default;
 
 std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay(
-        DisplayCreationArgs&& args) {
-    return compositionengine::impl::createDisplay(*this, std::move(args));
+        const DisplayCreationArgs& args) {
+    return compositionengine::impl::createDisplay(*this, args);
 }
 
-std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(LayerCreationArgs&& args) {
-    return compositionengine::impl::createLayer(*this, std::move(args));
+std::unique_ptr<compositionengine::LayerFECompositionState>
+CompositionEngine::createLayerFECompositionState() {
+    return std::make_unique<compositionengine::LayerFECompositionState>();
 }
 
 HWComposer& CompositionEngine::getHwComposer() const {
@@ -59,5 +72,92 @@
     mRenderEngine = std::move(renderEngine);
 }
 
+TimeStats& CompositionEngine::getTimeStats() const {
+    return *mTimeStats.get();
+}
+
+void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+    mTimeStats = timeStats;
+}
+
+bool CompositionEngine::needsAnotherUpdate() const {
+    return mNeedsAnotherUpdate;
+}
+
+nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const {
+    return mRefreshStartTime;
+}
+
+void CompositionEngine::present(CompositionRefreshArgs& args) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    preComposition(args);
+
+    {
+        // latchedLayers is used to track the set of front-end layer state that
+        // has been latched across all outputs for the prepare step, and is not
+        // needed for anything else.
+        LayerFESet latchedLayers;
+
+        for (const auto& output : args.outputs) {
+            output->prepare(args, latchedLayers);
+        }
+    }
+
+    updateLayerStateFromFE(args);
+
+    for (const auto& output : args.outputs) {
+        output->present(args);
+    }
+}
+
+void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) {
+    std::unordered_map<compositionengine::LayerFE*, compositionengine::LayerFECompositionState*>
+            uniqueVisibleLayers;
+
+    for (const auto& output : args.outputs) {
+        for (auto* layer : output->getOutputLayersOrderedByZ()) {
+            if (layer->isHardwareCursor()) {
+                // Latch the cursor composition state from each front-end layer.
+                layer->getLayerFE().prepareCompositionState(LayerFE::StateSubset::Cursor);
+                layer->writeCursorPositionToHWC();
+            }
+        }
+    }
+}
+
+void CompositionEngine::preComposition(CompositionRefreshArgs& args) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    bool needsAnotherUpdate = false;
+
+    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    for (auto& layer : args.layers) {
+        if (layer->onPreComposition(mRefreshStartTime)) {
+            needsAnotherUpdate = true;
+        }
+    }
+
+    mNeedsAnotherUpdate = needsAnotherUpdate;
+}
+
+void CompositionEngine::dump(std::string&) const {
+    // The base class has no state to dump, but derived classes might.
+}
+
+void CompositionEngine::setNeedsAnotherUpdateForTest(bool value) {
+    mNeedsAnotherUpdate = value;
+}
+
+void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) {
+    // Update the composition state from each front-end layer
+    for (const auto& output : args.outputs) {
+        output->updateLayerStateFromFE(args);
+    }
+}
+
 } // namespace impl
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index f9d70e3..1d8a23f 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -16,29 +16,39 @@
 
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayCreationArgs.h>
 #include <compositionengine/DisplaySurface.h>
+#include <compositionengine/LayerFE.h>
 #include <compositionengine/impl/Display.h>
 #include <compositionengine/impl/DisplayColorProfile.h>
 #include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/RenderSurface.h>
 
+#include <utils/Trace.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+#include "DisplayHardware/PowerAdvisor.h"
+
 namespace android::compositionengine::impl {
 
-std::shared_ptr<compositionengine::Display> createDisplay(
+std::shared_ptr<Display> createDisplay(
         const compositionengine::CompositionEngine& compositionEngine,
-        compositionengine::DisplayCreationArgs&& args) {
-    return std::make_shared<Display>(compositionEngine, std::move(args));
+        const compositionengine::DisplayCreationArgs& args) {
+    return createDisplayTemplated<Display>(compositionEngine, args);
 }
 
-Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
-      : compositionengine::impl::Output(compositionEngine),
-        mIsVirtual(args.isVirtual),
-        mId(args.displayId) {
-    editState().isSecure = args.isSecure;
-}
+Display::Display(const DisplayCreationArgs& args)
+      : mIsVirtual(args.isVirtual), mId(args.displayId), mPowerAdvisor(args.powerAdvisor) {}
 
 Display::~Display() = default;
 
@@ -54,6 +64,10 @@
     return mIsVirtual;
 }
 
+std::optional<DisplayId> Display::getDisplayId() const {
+    return mId;
+}
+
 void Display::disconnect() {
     if (!mId) {
         return;
@@ -64,19 +78,28 @@
     mId.reset();
 }
 
-void Display::setColorTransform(const mat4& transform) {
-    Output::setColorTransform(transform);
+void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
+    Output::setColorTransform(args);
+
+    if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
+        return;
+    }
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    status_t result = hwc.setColorTransform(*mId, transform);
+    status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);
     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) {
+void Display::setColorProfile(const ColorProfile& colorProfile) {
+    const ui::Dataspace targetDataspace =
+            getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
+                                                         colorProfile.colorSpaceAgnosticDataspace);
+
+    if (colorProfile.mode == getState().colorMode &&
+        colorProfile.dataspace == getState().dataspace &&
+        colorProfile.renderIntent == getState().renderIntent &&
+        targetDataspace == getState().targetDataspace) {
         return;
     }
 
@@ -85,10 +108,10 @@
         return;
     }
 
-    Output::setColorMode(mode, dataspace, renderIntent);
+    Output::setColorProfile(colorProfile);
 
     auto& hwc = getCompositionEngine().getHwComposer();
-    hwc.setActiveColorMode(*mId, mode, renderIntent);
+    hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent);
 }
 
 void Display::dump(std::string& out) const {
@@ -110,13 +133,221 @@
     Output::dumpBase(out);
 }
 
-void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) {
-    setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args)));
+void Display::createDisplayColorProfile(const DisplayColorProfileCreationArgs& args) {
+    setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(args));
 }
 
-void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) {
-    setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this,
-                                                                  std::move(args)));
+void Display::createRenderSurface(const RenderSurfaceCreationArgs& args) {
+    setRenderSurface(
+            compositionengine::impl::createRenderSurface(getCompositionEngine(), *this, args));
+}
+
+void Display::createClientCompositionCache(uint32_t cacheSize) {
+    cacheClientCompositionRequests(cacheSize);
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(
+        const sp<compositionengine::LayerFE>& layerFE) const {
+    auto result = impl::createOutputLayer(*this, layerFE);
+
+    if (result && mId) {
+        auto& hwc = getCompositionEngine().getHwComposer();
+        auto displayId = *mId;
+        // Note: For the moment we ensure it is safe to take a reference to the
+        // HWComposer implementation by destroying all the OutputLayers (and
+        // hence the HWC2::Layers they own) before setting a new HWComposer. See
+        // for example SurfaceFlinger::updateVrFlinger().
+        // TODO(b/121291683): Make this safer.
+        auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId),
+                                                     [&hwc, displayId](HWC2::Layer* layer) {
+                                                         hwc.destroyLayer(displayId, layer);
+                                                     });
+        ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",
+                 getName().c_str());
+        result->setHwcLayer(std::move(hwcLayer));
+    }
+    return result;
+}
+
+void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    Output::setReleasedLayers(refreshArgs);
+
+    if (!mId || refreshArgs.layersWithQueuedFrames.empty()) {
+        return;
+    }
+
+    // For layers that are being removed from a HWC display, and that have
+    // queued frames, add them to a a list of released layers so we can properly
+    // set a fence.
+    compositionengine::Output::ReleasedLayers releasedLayers;
+
+    // Any non-null entries in the current list of layers are layers that are no
+    // longer going to be visible
+    for (auto* outputLayer : getOutputLayersOrderedByZ()) {
+        if (!outputLayer) {
+            continue;
+        }
+
+        compositionengine::LayerFE* layerFE = &outputLayer->getLayerFE();
+        const bool hasQueuedFrames =
+                std::any_of(refreshArgs.layersWithQueuedFrames.cbegin(),
+                            refreshArgs.layersWithQueuedFrames.cend(),
+                            [layerFE](sp<compositionengine::LayerFE> layerWithQueuedFrames) {
+                                return layerFE == layerWithQueuedFrames.get();
+                            });
+
+        if (hasQueuedFrames) {
+            releasedLayers.emplace_back(layerFE);
+        }
+    }
+
+    setReleasedLayers(std::move(releasedLayers));
+}
+
+void Display::chooseCompositionStrategy() {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    // Default to the base settings -- client composition only.
+    Output::chooseCompositionStrategy();
+
+    // If we don't have a HWC display, then we are done
+    if (!mId) {
+        return;
+    }
+
+    // Get any composition changes requested by the HWC device, and apply them.
+    std::optional<android::HWComposer::DeviceRequestedChanges> changes;
+    auto& hwc = getCompositionEngine().getHwComposer();
+    if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(),
+                                                          &changes);
+        result != NO_ERROR) {
+        ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
+              strerror(-result));
+        return;
+    }
+    if (changes) {
+        applyChangedTypesToLayers(changes->changedTypes);
+        applyDisplayRequests(changes->displayRequests);
+        applyLayerRequestsToLayers(changes->layerRequests);
+    }
+
+    // Determine what type of composition we are doing from the final state
+    auto& state = editState();
+    state.usesClientComposition = anyLayersRequireClientComposition();
+    state.usesDeviceComposition = !allLayersRequireClientComposition();
+}
+
+bool Display::getSkipColorTransform() const {
+    if (!mId) {
+        return false;
+    }
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    return hwc.hasDisplayCapability(*mId, HWC2::DisplayCapability::SkipClientColorTransform);
+}
+
+bool Display::anyLayersRequireClientComposition() const {
+    const auto layers = getOutputLayersOrderedByZ();
+    return std::any_of(layers.begin(), layers.end(),
+                       [](const auto& layer) { return layer->requiresClientComposition(); });
+}
+
+bool Display::allLayersRequireClientComposition() const {
+    const auto layers = getOutputLayersOrderedByZ();
+    return std::all_of(layers.begin(), layers.end(),
+                       [](const auto& layer) { return layer->requiresClientComposition(); });
+}
+
+void Display::applyChangedTypesToLayers(const ChangedTypes& changedTypes) {
+    if (changedTypes.empty()) {
+        return;
+    }
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        auto hwcLayer = layer->getHwcLayer();
+        if (!hwcLayer) {
+            continue;
+        }
+
+        if (auto it = changedTypes.find(hwcLayer); it != changedTypes.end()) {
+            layer->applyDeviceCompositionTypeChange(
+                    static_cast<Hwc2::IComposerClient::Composition>(it->second));
+        }
+    }
+}
+
+void Display::applyDisplayRequests(const DisplayRequests& displayRequests) {
+    auto& state = editState();
+    state.flipClientTarget = (static_cast<uint32_t>(displayRequests) &
+                              static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0;
+    // Note: HWC2::DisplayRequest::WriteClientTargetToOutput is currently ignored.
+}
+
+void Display::applyLayerRequestsToLayers(const LayerRequests& layerRequests) {
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        layer->prepareForDeviceLayerRequests();
+
+        auto hwcLayer = layer->getHwcLayer();
+        if (!hwcLayer) {
+            continue;
+        }
+
+        if (auto it = layerRequests.find(hwcLayer); it != layerRequests.end()) {
+            layer->applyDeviceLayerRequest(
+                    static_cast<Hwc2::IComposerClient::LayerRequest>(it->second));
+        }
+    }
+}
+
+compositionengine::Output::FrameFences Display::presentAndGetFrameFences() {
+    auto result = impl::Output::presentAndGetFrameFences();
+
+    if (!mId) {
+        return result;
+    }
+
+    auto& hwc = getCompositionEngine().getHwComposer();
+    hwc.presentAndGetReleaseFences(*mId);
+
+    result.presentFence = hwc.getPresentFence(*mId);
+
+    // TODO(b/121291683): Change HWComposer call to return entire map
+    for (const auto* layer : getOutputLayersOrderedByZ()) {
+        auto hwcLayer = layer->getHwcLayer();
+        if (!hwcLayer) {
+            continue;
+        }
+
+        result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer));
+    }
+
+    hwc.clearReleaseFences(*mId);
+
+    return result;
+}
+
+void Display::setExpensiveRenderingExpected(bool enabled) {
+    Output::setExpensiveRenderingExpected(enabled);
+
+    if (mPowerAdvisor && mId) {
+        mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled);
+    }
+}
+
+void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    // 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)
+    if (!mId) {
+        if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) {
+            ALOGV("Skipping display composition");
+            return;
+        }
+    }
+
+    impl::Output::finishFrame(refreshArgs);
 }
 
 } // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
index 130ab1d..a7c4512 100644
--- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp
@@ -64,6 +64,12 @@
         RenderIntent::TONE_MAP_COLORIMETRIC,
 };
 
+// Returns true if the given colorMode is considered an HDR color mode
+bool isHdrColorMode(const ColorMode colorMode) {
+    return std::any_of(std::begin(sHdrColorModes), std::end(sHdrColorModes),
+                       [colorMode](ColorMode hdrColorMode) { return hdrColorMode == colorMode; });
+}
+
 // map known color mode to dataspace
 Dataspace colorModeToDataspace(ColorMode mode) {
     switch (mode) {
@@ -90,13 +96,7 @@
     candidates.push_back(mode);
 
     // check if mode is HDR
-    bool isHdr = false;
-    for (auto hdrMode : sHdrColorModes) {
-        if (hdrMode == mode) {
-            isHdr = true;
-            break;
-        }
-    }
+    bool isHdr = isHdrColorMode(mode);
 
     // add other HDR candidates when mode is HDR
     if (isHdr) {
@@ -184,11 +184,11 @@
 } // anonymous namespace
 
 std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile(
-        DisplayColorProfileCreationArgs&& args) {
-    return std::make_unique<DisplayColorProfile>(std::move(args));
+        const DisplayColorProfileCreationArgs& args) {
+    return std::make_unique<DisplayColorProfile>(args);
 }
 
-DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args)
+DisplayColorProfile::DisplayColorProfile(const DisplayColorProfileCreationArgs& args)
       : mHasWideColorGamut(args.hasWideColorGamut),
         mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) {
     populateColorModes(args.hwcColorModes);
@@ -376,6 +376,32 @@
     }
 }
 
+bool DisplayColorProfile::isDataspaceSupported(Dataspace dataspace) const {
+    switch (dataspace) {
+        case Dataspace::BT2020_PQ:
+        case Dataspace::BT2020_ITU_PQ:
+            return hasHDR10Support();
+
+        case Dataspace::BT2020_HLG:
+        case Dataspace::BT2020_ITU_HLG:
+            return hasHLGSupport();
+
+        default:
+            return true;
+    }
+}
+
+ui::Dataspace DisplayColorProfile::getTargetDataspace(ColorMode mode, Dataspace dataspace,
+                                                      Dataspace colorSpaceAgnosticDataspace) const {
+    if (isHdrColorMode(mode)) {
+        return Dataspace::UNKNOWN;
+    }
+    if (colorSpaceAgnosticDataspace != ui::Dataspace::UNKNOWN) {
+        return colorSpaceAgnosticDataspace;
+    }
+    return dataspace;
+}
+
 void DisplayColorProfile::dump(std::string& out) const {
     out.append("   Composition Display Color State:");
 
diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
index f72862b..cedc333 100644
--- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp
@@ -15,9 +15,17 @@
  */
 
 #include <compositionengine/impl/HwcBufferCache.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gui/BufferQueue.h>
 #include <ui/GraphicBuffer.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine::impl {
 
 HwcBufferCache::HwcBufferCache() {
@@ -31,7 +39,7 @@
         slot >= BufferQueue::NUM_BUFFER_SLOTS) {
         *outSlot = 0;
     } else {
-        *outSlot = slot;
+        *outSlot = static_cast<uint32_t>(slot);
     }
 
     auto& currentBuffer = mBuffers[*outSlot];
diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
deleted file mode 100644
index 96e9731..0000000
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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/LayerCreationArgs.h>
-#include <compositionengine/LayerFE.h>
-#include <compositionengine/impl/Layer.h>
-
-namespace android::compositionengine {
-
-Layer::~Layer() = default;
-
-namespace impl {
-
-std::shared_ptr<compositionengine::Layer> createLayer(
-        const compositionengine::CompositionEngine& compositionEngine,
-        compositionengine::LayerCreationArgs&& args) {
-    return std::make_shared<Layer>(compositionEngine, std::move(args));
-}
-
-Layer::Layer(const CompositionEngine& compositionEngine, LayerCreationArgs&& args)
-      : mCompositionEngine(compositionEngine), mLayerFE(args.layerFE) {
-    static_cast<void>(mCompositionEngine); // Temporary use to prevent an unused warning
-}
-
-Layer::~Layer() = default;
-
-sp<LayerFE> Layer::getLayerFE() const {
-    return mLayerFE.promote();
-}
-
-const LayerCompositionState& Layer::getState() const {
-    return mState;
-}
-
-LayerCompositionState& Layer::editState() {
-    return mState;
-}
-
-void Layer::dump(std::string& out) const {
-    auto layerFE = getLayerFE();
-    android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this,
-                                 layerFE ? layerFE->getDebugName() : "<unknown>");
-    mState.dump(out);
-}
-
-} // namespace impl
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
deleted file mode 100644
index 40c4da9..0000000
--- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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/impl/DumpHelpers.h>
-#include <compositionengine/impl/LayerCompositionState.h>
-
-namespace android::compositionengine::impl {
-
-namespace {
-
-using android::compositionengine::impl::dumpVal;
-
-void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color value) {
-    using android::base::StringAppendF;
-    StringAppendF(&out, "%s=[%d %d %d] ", name, value.r, value.g, value.b);
-}
-
-void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) {
-    out.append("      ");
-    dumpVal(out, "isSecure", state.isSecure);
-    dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop);
-    dumpVal(out, "geomBufferUsesDisplayInverseTransform",
-            state.geomBufferUsesDisplayInverseTransform);
-    dumpVal(out, "geomLayerTransform", state.geomLayerTransform);
-
-    out.append("\n      ");
-    dumpVal(out, "geomBufferSize", state.geomBufferSize);
-    dumpVal(out, "geomContentCrop", state.geomContentCrop);
-    dumpVal(out, "geomCrop", state.geomCrop);
-    dumpVal(out, "geomBufferTransform", state.geomBufferTransform);
-
-    out.append("\n      ");
-    dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion);
-
-    out.append("      ");
-    dumpVal(out, "geomLayerBounds", state.geomLayerBounds);
-
-    out.append("\n      ");
-    dumpVal(out, "blend", toString(state.blendMode), state.blendMode);
-    dumpVal(out, "alpha", state.alpha);
-
-    out.append("\n      ");
-    dumpVal(out, "type", state.type);
-    dumpVal(out, "appId", state.appId);
-
-    dumpVal(out, "composition type", toString(state.compositionType), state.compositionType);
-
-    out.append("\n      buffer: ");
-    dumpVal(out, "buffer", state.buffer.get());
-    dumpVal(out, "slot", state.bufferSlot);
-
-    out.append("\n      ");
-    dumpVal(out, "sideband stream", state.sidebandStream.get());
-
-    out.append("\n      ");
-    dumpVal(out, "color", state.color);
-
-    out.append("\n      ");
-    dumpVal(out, "dataspace", toString(state.dataspace), state.dataspace);
-    dumpVal(out, "hdr metadata types", state.hdrMetadata.validTypes);
-    dumpVal(out, "colorTransform", state.colorTransform);
-
-    out.append("\n");
-}
-
-} // namespace
-
-void LayerCompositionState::dump(std::string& out) const {
-    out.append("    frontend:\n");
-    dumpFrontEnd(out, frontEnd);
-}
-
-} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
new file mode 100644
index 0000000..02e3a45
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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/LayerFECompositionState.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+namespace android::compositionengine {
+
+namespace {
+
+using android::compositionengine::impl::dumpVal;
+
+void dumpVal(std::string& out, const char* name, half4 value) {
+    using android::base::StringAppendF;
+    StringAppendF(&out, "%s=[%f %f %f] ", name, static_cast<float>(value.r),
+                  static_cast<float>(value.g), static_cast<float>(value.b));
+}
+
+} // namespace
+
+std::string GenericLayerMetadataEntry::dumpAsString() const {
+    using android::base::StringAppendF;
+    std::string out;
+
+    out.append("GenericLayerMetadataEntry{mandatory: ");
+    StringAppendF(&out, "%d", mandatory);
+    out.append(" value: ");
+    for (uint8_t byte : value) {
+        StringAppendF(&out, "0x08%" PRIx8 " ", byte);
+    }
+    out.append("]}");
+    return out;
+}
+
+LayerFECompositionState::~LayerFECompositionState() = default;
+
+void LayerFECompositionState::dump(std::string& out) const {
+    out.append("      ");
+    dumpVal(out, "isSecure", isSecure);
+    dumpVal(out, "geomUsesSourceCrop", geomUsesSourceCrop);
+    dumpVal(out, "geomBufferUsesDisplayInverseTransform", geomBufferUsesDisplayInverseTransform);
+    dumpVal(out, "geomLayerTransform", geomLayerTransform);
+
+    out.append("\n      ");
+    dumpVal(out, "geomBufferSize", geomBufferSize);
+    dumpVal(out, "geomContentCrop", geomContentCrop);
+    dumpVal(out, "geomCrop", geomCrop);
+    dumpVal(out, "geomBufferTransform", geomBufferTransform);
+
+    out.append("\n      ");
+    dumpVal(out, "transparentRegionHint", transparentRegionHint);
+
+    out.append("      ");
+    dumpVal(out, "geomLayerBounds", geomLayerBounds);
+
+    out.append("      ");
+    dumpVal(out, "shadowRadius", shadowRadius);
+
+    out.append("\n      ");
+    dumpVal(out, "blend", toString(blendMode), blendMode);
+    dumpVal(out, "alpha", alpha);
+    dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius);
+
+    out.append("\n      ");
+    dumpVal(out, "type", type);
+    dumpVal(out, "appId", appId);
+
+    if (!metadata.empty()) {
+        out.append("\n      metadata {");
+        for (const auto& [key, entry] : metadata) {
+            out.append("\n           ");
+            out.append(key);
+            out.append("=");
+            out.append(entry.dumpAsString());
+        }
+        out.append("\n      }\n      ");
+    }
+
+    dumpVal(out, "composition type", toString(compositionType), compositionType);
+
+    out.append("\n      buffer: ");
+    dumpVal(out, "slot", bufferSlot);
+    dumpVal(out, "buffer", buffer.get());
+
+    out.append("\n      ");
+    dumpVal(out, "sideband stream", sidebandStream.get());
+
+    out.append("\n      ");
+    dumpVal(out, "color", color);
+
+    out.append("\n      ");
+    dumpVal(out, "isOpaque", isOpaque);
+    dumpVal(out, "hasProtectedContent", hasProtectedContent);
+    dumpVal(out, "isColorspaceAgnostic", isColorspaceAgnostic);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
+    dumpVal(out, "hdr metadata types", hdrMetadata.validTypes);
+    dumpVal(out, "colorTransform", colorTransform);
+
+    out.append("\n");
+}
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 01b5781..e792f45 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -14,14 +14,35 @@
  * limitations under the License.
  */
 
+#include <thread>
+
 #include <android-base/stringprintf.h>
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/RenderSurface.h>
 #include <compositionengine/impl/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/RenderEngine.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 #include <ui/DebugUtils.h>
+#include <ui/HdrCapabilities.h>
+#include <utils/Trace.h>
+
+#include "TracedOrdinal.h"
 
 namespace android::compositionengine {
 
@@ -29,20 +50,43 @@
 
 namespace impl {
 
-Output::Output(const CompositionEngine& compositionEngine)
-      : mCompositionEngine(compositionEngine) {}
+namespace {
+
+template <typename T>
+class Reversed {
+public:
+    explicit Reversed(const T& container) : mContainer(container) {}
+    auto begin() { return mContainer.rbegin(); }
+    auto end() { return mContainer.rend(); }
+
+private:
+    const T& mContainer;
+};
+
+// Helper for enumerating over a container in reverse order
+template <typename T>
+Reversed<T> reversed(const T& c) {
+    return Reversed<T>(c);
+}
+
+} // namespace
+
+std::shared_ptr<Output> createOutput(
+        const compositionengine::CompositionEngine& compositionEngine) {
+    return createOutputTemplated<Output>(compositionEngine);
+}
 
 Output::~Output() = default;
 
-const CompositionEngine& Output::getCompositionEngine() const {
-    return mCompositionEngine;
-}
-
 bool Output::isValid() const {
     return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface &&
             mRenderSurface->isValid();
 }
 
+std::optional<DisplayId> Output::getDisplayId() const {
+    return {};
+}
+
 const std::string& Output::getName() const {
     return mName;
 }
@@ -52,73 +96,81 @@
 }
 
 void Output::setCompositionEnabled(bool enabled) {
-    if (mState.isEnabled == enabled) {
+    auto& outputState = editState();
+    if (outputState.isEnabled == enabled) {
         return;
     }
 
-    mState.isEnabled = enabled;
+    outputState.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;
+void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame,
+                           const Rect& viewport, const Rect& sourceClip,
+                           const Rect& destinationClip, bool needsFiltering) {
+    auto& outputState = editState();
+    outputState.transform = transform;
+    outputState.orientation = orientation;
+    outputState.sourceClip = sourceClip;
+    outputState.destinationClip = destinationClip;
+    outputState.frame = frame;
+    outputState.viewport = viewport;
+    outputState.needsFiltering = needsFiltering;
 
     dirtyEntireOutput();
 }
 
-// TODO(lpique): Rename setSize() once more is moved.
+// TODO(b/121291683): 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());
+    // TODO(b/121291683): Rename outputState.size once more is moved.
+    editState().bounds = Rect(mRenderSurface->getSize());
 
     dirtyEntireOutput();
 }
 
 void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) {
-    mState.layerStackId = layerStackId;
-    mState.layerStackInternal = isInternal;
+    auto& outputState = editState();
+    outputState.layerStackId = layerStackId;
+    outputState.layerStackInternal = isInternal;
 
     dirtyEntireOutput();
 }
 
-void Output::setColorTransform(const mat4& transform) {
-    if (mState.colorTransformMat == transform) {
+void Output::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
+    auto& colorTransformMatrix = editState().colorTransformMatrix;
+    if (!args.colorTransformMatrix || colorTransformMatrix == args.colorTransformMatrix) {
         return;
     }
 
-    const bool isIdentity = (transform == mat4());
-    const auto newColorTransform =
-            isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
-
-    mState.colorTransform = newColorTransform;
-    mState.colorTransformMat = transform;
+    colorTransformMatrix = *args.colorTransformMatrix;
 
     dirtyEntireOutput();
 }
 
-void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
-                          ui::RenderIntent renderIntent) {
-    if (mState.colorMode == mode && mState.dataspace == dataspace &&
-        mState.renderIntent == renderIntent) {
+void Output::setColorProfile(const ColorProfile& colorProfile) {
+    ui::Dataspace targetDataspace =
+            getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace,
+                                                         colorProfile.colorSpaceAgnosticDataspace);
+
+    auto& outputState = editState();
+    if (outputState.colorMode == colorProfile.mode &&
+        outputState.dataspace == colorProfile.dataspace &&
+        outputState.renderIntent == colorProfile.renderIntent &&
+        outputState.targetDataspace == targetDataspace) {
         return;
     }
 
-    mState.colorMode = mode;
-    mState.dataspace = dataspace;
-    mState.renderIntent = renderIntent;
+    outputState.colorMode = colorProfile.mode;
+    outputState.dataspace = colorProfile.dataspace;
+    outputState.renderIntent = colorProfile.renderIntent;
+    outputState.targetDataspace = targetDataspace;
 
-    mRenderSurface->setBufferDataspace(dataspace);
+    mRenderSurface->setBufferDataspace(colorProfile.dataspace);
 
     ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
-          decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
-          renderIntent);
+          decodeColorMode(colorProfile.mode).c_str(), colorProfile.mode,
+          decodeRenderIntent(colorProfile.renderIntent).c_str(), colorProfile.renderIntent);
 
     dirtyEntireOutput();
 }
@@ -134,7 +186,7 @@
 }
 
 void Output::dumpBase(std::string& out) const {
-    mState.dump(out);
+    dumpState(out);
 
     if (mDisplayColorProfile) {
         mDisplayColorProfile->dump(out);
@@ -148,8 +200,8 @@
         out.append("    No render surface!\n");
     }
 
-    android::base::StringAppendF(&out, "\n   %zu Layers\b", mOutputLayersOrderedByZ.size());
-    for (const auto& outputLayer : mOutputLayersOrderedByZ) {
+    android::base::StringAppendF(&out, "\n   %zu Layers\n", getOutputLayerCount());
+    for (const auto* outputLayer : getOutputLayersOrderedByZ()) {
         if (!outputLayer) {
             continue;
         }
@@ -165,6 +217,10 @@
     mDisplayColorProfile = std::move(mode);
 }
 
+const Output::ReleasedLayers& Output::getReleasedLayersForTest() const {
+    return mReleasedLayers;
+}
+
 void Output::setDisplayColorProfileForTest(
         std::unique_ptr<compositionengine::DisplayColorProfile> mode) {
     mDisplayColorProfile = std::move(mode);
@@ -176,68 +232,877 @@
 
 void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) {
     mRenderSurface = std::move(surface);
-    mState.bounds = Rect(mRenderSurface->getSize());
+    editState().bounds = Rect(mRenderSurface->getSize());
 
     dirtyEntireOutput();
 }
 
+void Output::cacheClientCompositionRequests(uint32_t cacheSize) {
+    if (cacheSize == 0) {
+        mClientCompositionRequestCache.reset();
+    } else {
+        mClientCompositionRequestCache = std::make_unique<ClientCompositionRequestCache>(cacheSize);
+    }
+};
+
 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);
+    const auto& outputState = getState();
+    Region dirty(outputState.viewport);
     if (!repaintEverything) {
-        dirty.andSelf(mState.dirtyRegion);
+        dirty.andSelf(outputState.dirtyRegion);
     }
     return dirty;
 }
 
-bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const {
+bool Output::belongsInOutput(std::optional<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);
+    const auto& outputState = getState();
+    return layerStackId && (*layerStackId == outputState.layerStackId) &&
+            (!internalOnly || outputState.layerStackInternal);
 }
 
-compositionengine::OutputLayer* Output::getOutputLayerForLayer(
-        compositionengine::Layer* layer) const {
-    for (const auto& outputLayer : mOutputLayersOrderedByZ) {
-        if (outputLayer && &outputLayer->getLayer() == layer) {
-            return outputLayer.get();
+bool Output::belongsInOutput(const sp<compositionengine::LayerFE>& layerFE) const {
+    const auto* layerFEState = layerFE->getCompositionState();
+    return layerFEState && belongsInOutput(layerFEState->layerStackId, layerFEState->internalOnly);
+}
+
+std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer(
+        const sp<LayerFE>& layerFE) const {
+    return impl::createOutputLayer(*this, layerFE);
+}
+
+compositionengine::OutputLayer* Output::getOutputLayerForLayer(const sp<LayerFE>& layerFE) const {
+    auto index = findCurrentOutputLayerForLayer(layerFE);
+    return index ? getOutputLayerOrderedByZByIndex(*index) : nullptr;
+}
+
+std::optional<size_t> Output::findCurrentOutputLayerForLayer(
+        const sp<compositionengine::LayerFE>& layer) const {
+    for (size_t i = 0; i < getOutputLayerCount(); i++) {
+        auto outputLayer = getOutputLayerOrderedByZByIndex(i);
+        if (outputLayer && &outputLayer->getLayerFE() == layer.get()) {
+            return i;
         }
     }
-    return nullptr;
+    return std::nullopt;
 }
 
-std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer(
-        std::optional<DisplayId> displayId, std::shared_ptr<compositionengine::Layer> layer,
-        sp<compositionengine::LayerFE> layerFE) {
-    for (auto& outputLayer : mOutputLayersOrderedByZ) {
-        if (outputLayer && &outputLayer->getLayer() == layer.get()) {
-            return std::move(outputLayer);
+void Output::setReleasedLayers(Output::ReleasedLayers&& layers) {
+    mReleasedLayers = std::move(layers);
+}
+
+void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                     LayerFESet& geomSnapshots) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    rebuildLayerStacks(refreshArgs, geomSnapshots);
+}
+
+void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    updateColorProfile(refreshArgs);
+    updateAndWriteCompositionState(refreshArgs);
+    setColorTransform(refreshArgs);
+    beginFrame();
+    prepareFrame();
+    devOptRepaintFlash(refreshArgs);
+    finishFrame(refreshArgs);
+    postFramebuffer();
+}
+
+void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                LayerFESet& layerFESet) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    auto& outputState = editState();
+
+    // Do nothing if this output is not enabled or there is no need to perform this update
+    if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) {
+        return;
+    }
+
+    // Process the layers to determine visibility and coverage
+    compositionengine::Output::CoverageState coverage{layerFESet};
+    collectVisibleLayers(refreshArgs, coverage);
+
+    // Compute the resulting coverage for this output, and store it for later
+    const ui::Transform& tr = outputState.transform;
+    Region undefinedRegion{outputState.bounds};
+    undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers));
+
+    outputState.undefinedRegion = undefinedRegion;
+    outputState.dirtyRegion.orSelf(coverage.dirtyRegion);
+}
+
+void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,
+                                  compositionengine::Output::CoverageState& coverage) {
+    // Evaluate the layers from front to back to determine what is visible. This
+    // also incrementally calculates the coverage information for each layer as
+    // well as the entire output.
+    for (auto layer : reversed(refreshArgs.layers)) {
+        // Incrementally process the coverage for each layer
+        ensureOutputLayerIfVisible(layer, coverage);
+
+        // TODO(b/121291683): Stop early if the output is completely covered and
+        // no more layers could even be visible underneath the ones on top.
+    }
+
+    setReleasedLayers(refreshArgs);
+
+    finalizePendingOutputLayers();
+
+    // Generate a simple Z-order values to each visible output layer
+    uint32_t zOrder = 0;
+    for (auto* outputLayer : getOutputLayersOrderedByZ()) {
+        outputLayer->editState().z = zOrder++;
+    }
+}
+
+void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
+                                        compositionengine::Output::CoverageState& coverage) {
+    // Ensure we have a snapshot of the basic geometry layer state. Limit the
+    // snapshots to once per frame for each candidate layer, as layers may
+    // appear on multiple outputs.
+    if (!coverage.latchedLayers.count(layerFE)) {
+        coverage.latchedLayers.insert(layerFE);
+        layerFE->prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry);
+    }
+
+    // Only consider the layers on the given layer stack
+    if (!belongsInOutput(layerFE)) {
+        return;
+    }
+
+    // Obtain a read-only pointer to the front-end layer state
+    const auto* layerFEState = layerFE->getCompositionState();
+    if (CC_UNLIKELY(!layerFEState)) {
+        return;
+    }
+
+    // handle hidden surfaces by setting the visible region to empty
+    if (CC_UNLIKELY(!layerFEState->isVisible)) {
+        return;
+    }
+
+    /*
+     * opaqueRegion: area of a surface that is fully opaque.
+     */
+    Region opaqueRegion;
+
+    /*
+     * visibleRegion: area of a surface that is visible on screen and not fully
+     * transparent. This is essentially the layer's footprint minus the opaque
+     * regions above it. Areas covered by a translucent surface are considered
+     * visible.
+     */
+    Region visibleRegion;
+
+    /*
+     * coveredRegion: area of a surface that is covered by all visible regions
+     * above it (which includes the translucent areas).
+     */
+    Region coveredRegion;
+
+    /*
+     * transparentRegion: area of a surface that is hinted to be completely
+     * transparent. This is only used to tell when the layer has no visible non-
+     * transparent regions and can be removed from the layer list. It does not
+     * affect the visibleRegion of this layer or any layers beneath it. The hint
+     * may not be correct if apps don't respect the SurfaceView restrictions
+     * (which, sadly, some don't).
+     */
+    Region transparentRegion;
+
+    /*
+     * shadowRegion: Region cast by the layer's shadow.
+     */
+    Region shadowRegion;
+
+    const ui::Transform& tr = layerFEState->geomLayerTransform;
+
+    // Get the visible region
+    // TODO(b/121291683): Is it worth creating helper methods on LayerFEState
+    // for computations like this?
+    const Rect visibleRect(tr.transform(layerFEState->geomLayerBounds));
+    visibleRegion.set(visibleRect);
+
+    if (layerFEState->shadowRadius > 0.0f) {
+        // if the layer casts a shadow, offset the layers visible region and
+        // calculate the shadow region.
+        const auto inset = static_cast<int32_t>(ceilf(layerFEState->shadowRadius) * -1.0f);
+        Rect visibleRectWithShadows(visibleRect);
+        visibleRectWithShadows.inset(inset, inset, inset, inset);
+        visibleRegion.set(visibleRectWithShadows);
+        shadowRegion = visibleRegion.subtract(visibleRect);
+    }
+
+    if (visibleRegion.isEmpty()) {
+        return;
+    }
+
+    // Remove the transparent area from the visible region
+    if (!layerFEState->isOpaque) {
+        if (tr.preserveRects()) {
+            // transform the transparent region
+            transparentRegion = tr.transform(layerFEState->transparentRegionHint);
+        } else {
+            // transformation too complex, can't do the
+            // transparent region optimization.
+            transparentRegion.clear();
         }
     }
-    return createOutputLayer(mCompositionEngine, displayId, *this, layer, layerFE);
+
+    // compute the opaque region
+    const auto layerOrientation = tr.getOrientation();
+    if (layerFEState->isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) {
+        // If we one of the simple category of transforms (0/90/180/270 rotation
+        // + any flip), then the opaque region is the layer's footprint.
+        // Otherwise we don't try and compute the opaque region since there may
+        // be errors at the edges, and we treat the entire layer as
+        // translucent.
+        opaqueRegion.set(visibleRect);
+    }
+
+    // Clip the covered region to the visible region
+    coveredRegion = coverage.aboveCoveredLayers.intersect(visibleRegion);
+
+    // Update accumAboveCoveredLayers for next (lower) layer
+    coverage.aboveCoveredLayers.orSelf(visibleRegion);
+
+    // subtract the opaque region covered by the layers above us
+    visibleRegion.subtractSelf(coverage.aboveOpaqueLayers);
+
+    if (visibleRegion.isEmpty()) {
+        return;
+    }
+
+    // Get coverage information for the layer as previously displayed,
+    // also taking over ownership from mOutputLayersorderedByZ.
+    auto prevOutputLayerIndex = findCurrentOutputLayerForLayer(layerFE);
+    auto prevOutputLayer =
+            prevOutputLayerIndex ? getOutputLayerOrderedByZByIndex(*prevOutputLayerIndex) : nullptr;
+
+    //  Get coverage information for the layer as previously displayed
+    // TODO(b/121291683): Define kEmptyRegion as a constant in Region.h
+    const Region kEmptyRegion;
+    const Region& oldVisibleRegion =
+            prevOutputLayer ? prevOutputLayer->getState().visibleRegion : kEmptyRegion;
+    const Region& oldCoveredRegion =
+            prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion;
+
+    // compute this layer's dirty region
+    Region dirty;
+    if (layerFEState->contentDirty) {
+        // we need to invalidate the whole region
+        dirty = visibleRegion;
+        // as well, as the old visible region
+        dirty.orSelf(oldVisibleRegion);
+    } else {
+        /* compute the exposed region:
+         *   the exposed region consists of two components:
+         *   1) what's VISIBLE now and was COVERED before
+         *   2) what's EXPOSED now less what was EXPOSED before
+         *
+         * note that (1) is conservative, we start with the whole visible region
+         * but only keep what used to be covered by something -- which mean it
+         * may have been exposed.
+         *
+         * (2) handles areas that were not covered by anything but got exposed
+         * because of a resize.
+         *
+         */
+        const Region newExposed = visibleRegion - coveredRegion;
+        const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
+        dirty = (visibleRegion & oldCoveredRegion) | (newExposed - oldExposed);
+    }
+    dirty.subtractSelf(coverage.aboveOpaqueLayers);
+
+    // accumulate to the screen dirty region
+    coverage.dirtyRegion.orSelf(dirty);
+
+    // Update accumAboveOpaqueLayers for next (lower) layer
+    coverage.aboveOpaqueLayers.orSelf(opaqueRegion);
+
+    // Compute the visible non-transparent region
+    Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
+
+    // Perform the final check to see if this layer is visible on this output
+    // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
+    const auto& outputState = getState();
+    Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
+    drawRegion.andSelf(outputState.bounds);
+    if (drawRegion.isEmpty()) {
+        return;
+    }
+
+    Region visibleNonShadowRegion = visibleRegion.subtract(shadowRegion);
+
+    // The layer is visible. Either reuse the existing outputLayer if we have
+    // one, or create a new one if we do not.
+    auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
+
+    // Store the layer coverage information into the layer state as some of it
+    // is useful later.
+    auto& outputLayerState = result->editState();
+    outputLayerState.visibleRegion = visibleRegion;
+    outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
+    outputLayerState.coveredRegion = coveredRegion;
+    outputLayerState.outputSpaceVisibleRegion =
+            outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+    outputLayerState.shadowRegion = shadowRegion;
 }
 
-void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) {
-    mOutputLayersOrderedByZ = std::move(layers);
+void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
+    // The base class does nothing with this call.
 }
 
-const Output::OutputLayers& Output::getOutputLayersOrderedByZ() const {
-    return mOutputLayersOrderedByZ;
+void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const {
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        layer->getLayerFE().prepareCompositionState(
+                args.updatingGeometryThisFrame ? LayerFE::StateSubset::GeometryAndContent
+                                               : LayerFE::StateSubset::Content);
+    }
+}
+
+void Output::updateAndWriteCompositionState(
+        const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    if (!getState().isEnabled) {
+        return;
+    }
+
+    mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition();
+    bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr;
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,
+                                      refreshArgs.devOptForceClientComposition ||
+                                              forceClientComposition);
+
+        if (mLayerRequestingBackgroundBlur == layer) {
+            forceClientComposition = false;
+        }
+
+        // Send the updated state to the HWC, if appropriate.
+        layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
+    }
+}
+
+compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const {
+    compositionengine::OutputLayer* layerRequestingBgComposition = nullptr;
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) {
+            layerRequestingBgComposition = layer;
+        }
+    }
+    return layerRequestingBgComposition;
+}
+
+void Output::updateColorProfile(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    setColorProfile(pickColorProfile(refreshArgs));
+}
+
+// Returns a data space that fits all visible layers.  The returned data space
+// 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
+ui::Dataspace Output::getBestDataspace(ui::Dataspace* outHdrDataSpace,
+                                       bool* outIsHdrClientComposition) const {
+    ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB;
+    *outHdrDataSpace = ui::Dataspace::UNKNOWN;
+
+    for (const auto* layer : getOutputLayersOrderedByZ()) {
+        switch (layer->getLayerFE().getCompositionState()->dataspace) {
+            case ui::Dataspace::V0_SCRGB:
+            case ui::Dataspace::V0_SCRGB_LINEAR:
+            case ui::Dataspace::BT2020:
+            case ui::Dataspace::BT2020_ITU:
+            case ui::Dataspace::BT2020_LINEAR:
+            case ui::Dataspace::DISPLAY_BT2020:
+                bestDataSpace = ui::Dataspace::DISPLAY_BT2020;
+                break;
+            case ui::Dataspace::DISPLAY_P3:
+                bestDataSpace = ui::Dataspace::DISPLAY_P3;
+                break;
+            case ui::Dataspace::BT2020_PQ:
+            case ui::Dataspace::BT2020_ITU_PQ:
+                bestDataSpace = ui::Dataspace::DISPLAY_P3;
+                *outHdrDataSpace = ui::Dataspace::BT2020_PQ;
+                *outIsHdrClientComposition =
+                        layer->getLayerFE().getCompositionState()->forceClientComposition;
+                break;
+            case ui::Dataspace::BT2020_HLG:
+            case ui::Dataspace::BT2020_ITU_HLG:
+                bestDataSpace = ui::Dataspace::DISPLAY_P3;
+                // When there's mixed PQ content and HLG content, we set the HDR
+                // data space to be BT2020_PQ and convert HLG to PQ.
+                if (*outHdrDataSpace == ui::Dataspace::UNKNOWN) {
+                    *outHdrDataSpace = ui::Dataspace::BT2020_HLG;
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    return bestDataSpace;
+}
+
+compositionengine::Output::ColorProfile Output::pickColorProfile(
+        const compositionengine::CompositionRefreshArgs& refreshArgs) const {
+    if (refreshArgs.outputColorSetting == OutputColorSetting::kUnmanaged) {
+        return ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                            ui::RenderIntent::COLORIMETRIC,
+                            refreshArgs.colorSpaceAgnosticDataspace};
+    }
+
+    ui::Dataspace hdrDataSpace;
+    bool isHdrClientComposition = false;
+    ui::Dataspace bestDataSpace = getBestDataspace(&hdrDataSpace, &isHdrClientComposition);
+
+    switch (refreshArgs.forceOutputColorMode) {
+        case ui::ColorMode::SRGB:
+            bestDataSpace = ui::Dataspace::V0_SRGB;
+            break;
+        case ui::ColorMode::DISPLAY_P3:
+            bestDataSpace = ui::Dataspace::DISPLAY_P3;
+            break;
+        default:
+            break;
+    }
+
+    // respect hdrDataSpace only when there is no legacy HDR support
+    const bool isHdr = hdrDataSpace != ui::Dataspace::UNKNOWN &&
+            !mDisplayColorProfile->hasLegacyHdrSupport(hdrDataSpace) && !isHdrClientComposition;
+    if (isHdr) {
+        bestDataSpace = hdrDataSpace;
+    }
+
+    ui::RenderIntent intent;
+    switch (refreshArgs.outputColorSetting) {
+        case OutputColorSetting::kManaged:
+        case OutputColorSetting::kUnmanaged:
+            intent = isHdr ? ui::RenderIntent::TONE_MAP_COLORIMETRIC
+                           : ui::RenderIntent::COLORIMETRIC;
+            break;
+        case OutputColorSetting::kEnhanced:
+            intent = isHdr ? ui::RenderIntent::TONE_MAP_ENHANCE : ui::RenderIntent::ENHANCE;
+            break;
+        default: // vendor display color setting
+            intent = static_cast<ui::RenderIntent>(refreshArgs.outputColorSetting);
+            break;
+    }
+
+    ui::ColorMode outMode;
+    ui::Dataspace outDataSpace;
+    ui::RenderIntent outRenderIntent;
+    mDisplayColorProfile->getBestColorMode(bestDataSpace, intent, &outDataSpace, &outMode,
+                                           &outRenderIntent);
+
+    return ColorProfile{outMode, outDataSpace, outRenderIntent,
+                        refreshArgs.colorSpaceAgnosticDataspace};
+}
+
+void Output::beginFrame() {
+    auto& outputState = editState();
+    const bool dirty = !getDirtyRegion(false).isEmpty();
+    const bool empty = getOutputLayerCount() == 0;
+    const bool wasEmpty = !outputState.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.
+    const bool mustRecompose = dirty && !(empty && wasEmpty);
+
+    const char flagPrefix[] = {'-', '+'};
+    static_cast<void>(flagPrefix);
+    ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__,
+             mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty],
+             flagPrefix[empty], flagPrefix[wasEmpty]);
+
+    mRenderSurface->beginFrame(mustRecompose);
+
+    if (mustRecompose) {
+        outputState.lastCompositionHadVisibleLayers = !empty;
+    }
+}
+
+void Output::prepareFrame() {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    const auto& outputState = getState();
+    if (!outputState.isEnabled) {
+        return;
+    }
+
+    chooseCompositionStrategy();
+
+    mRenderSurface->prepareFrame(outputState.usesClientComposition,
+                                 outputState.usesDeviceComposition);
+}
+
+void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) {
+    if (CC_LIKELY(!refreshArgs.devOptFlashDirtyRegionsDelay)) {
+        return;
+    }
+
+    if (getState().isEnabled) {
+        // transform the dirty region into this screen's coordinate space
+        const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything);
+        if (!dirtyRegion.isEmpty()) {
+            base::unique_fd readyFence;
+            // redraw the whole screen
+            static_cast<void>(composeSurfaces(dirtyRegion));
+
+            mRenderSurface->queueBuffer(std::move(readyFence));
+        }
+    }
+
+    postFramebuffer();
+
+    std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
+
+    prepareFrame();
+}
+
+void Output::finishFrame(const compositionengine::CompositionRefreshArgs&) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    if (!getState().isEnabled) {
+        return;
+    }
+
+    // Repaint the framebuffer (if needed), getting the optional fence for when
+    // the composition completes.
+    auto optReadyFence = composeSurfaces(Region::INVALID_REGION);
+    if (!optReadyFence) {
+        return;
+    }
+
+    // swap buffers (presentation)
+    mRenderSurface->queueBuffer(std::move(*optReadyFence));
+}
+
+std::optional<base::unique_fd> Output::composeSurfaces(const Region& debugRegion) {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    const auto& outputState = getState();
+    OutputCompositionState& outputCompositionState = editState();
+    const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition",
+                                                      outputState.usesClientComposition};
+    base::unique_fd readyFence;
+    if (!hasClientComposition) {
+        setExpensiveRenderingExpected(false);
+        return readyFence;
+    }
+
+    ALOGV("hasClientComposition");
+
+    auto& renderEngine = getCompositionEngine().getRenderEngine();
+    const bool supportsProtectedContent = renderEngine.supportsProtectedContent();
+
+    renderengine::DisplaySettings clientCompositionDisplay;
+    clientCompositionDisplay.physicalDisplay = outputState.destinationClip;
+    clientCompositionDisplay.clip = outputState.sourceClip;
+    clientCompositionDisplay.globalTransform = outputState.transform.asMatrix4();
+    clientCompositionDisplay.orientation = outputState.orientation;
+    clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut()
+            ? outputState.dataspace
+            : ui::Dataspace::UNKNOWN;
+    clientCompositionDisplay.maxLuminance =
+            mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
+
+    // Compute the global color transform matrix.
+    if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
+        clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
+    }
+
+    // Note: Updated by generateClientCompositionRequests
+    clientCompositionDisplay.clearRegion = Region::INVALID_REGION;
+
+    // Generate the client composition requests for the layers on this output.
+    std::vector<LayerFE::LayerSettings> clientCompositionLayers =
+            generateClientCompositionRequests(supportsProtectedContent,
+                                              clientCompositionDisplay.clearRegion,
+                                              clientCompositionDisplay.outputDataspace);
+    appendRegionFlashRequests(debugRegion, clientCompositionLayers);
+
+    // If we the display is secure, protected content support is enabled, and at
+    // least one layer has protected content, we need to use a secure back
+    // buffer.
+    if (outputState.isSecure && supportsProtectedContent) {
+        auto layers = getOutputLayersOrderedByZ();
+        bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) {
+            return layer->getLayerFE().getCompositionState()->hasProtectedContent;
+        });
+        if (needsProtected != renderEngine.isProtected()) {
+            renderEngine.useProtectedContext(needsProtected);
+        }
+        if (needsProtected != mRenderSurface->isProtected() &&
+            needsProtected == renderEngine.isProtected()) {
+            mRenderSurface->setProtected(needsProtected);
+        }
+    }
+
+    base::unique_fd fd;
+    sp<GraphicBuffer> buf = mRenderSurface->dequeueBuffer(&fd);
+    if (buf == nullptr) {
+        ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
+              "client composition for this frame",
+              mName.c_str());
+        return std::nullopt;
+    }
+
+    // Check if the client composition requests were rendered into the provided graphic buffer. If
+    // so, we can reuse the buffer and avoid client composition.
+    if (mClientCompositionRequestCache) {
+        if (mClientCompositionRequestCache->exists(buf->getId(), clientCompositionDisplay,
+                                                   clientCompositionLayers)) {
+            outputCompositionState.reusedClientComposition = true;
+            setExpensiveRenderingExpected(false);
+            return readyFence;
+        }
+        mClientCompositionRequestCache->add(buf->getId(), clientCompositionDisplay,
+                                            clientCompositionLayers);
+    }
+
+    // We boost GPU frequency here because there will be color spaces conversion
+    // or complex GPU shaders and it's expensive. We boost the GPU frequency so that
+    // GPU composition can finish in time. We must reset GPU frequency afterwards,
+    // because high frequency consumes extra battery.
+    const bool expensiveRenderingExpected =
+            clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3;
+    if (expensiveRenderingExpected) {
+        setExpensiveRenderingExpected(true);
+    }
+
+    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers;
+    clientCompositionLayerPointers.reserve(clientCompositionLayers.size());
+    std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+                   std::back_inserter(clientCompositionLayerPointers),
+                   [](LayerFE::LayerSettings& settings) -> renderengine::LayerSettings* {
+                       return &settings;
+                   });
+
+    const nsecs_t renderEngineStart = systemTime();
+    status_t status =
+            renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers,
+                                    buf->getNativeBuffer(), /*useFramebufferCache=*/true,
+                                    std::move(fd), &readyFence);
+
+    if (status != NO_ERROR && mClientCompositionRequestCache) {
+        // If rendering was not successful, remove the request from the cache.
+        mClientCompositionRequestCache->remove(buf->getId());
+    }
+
+    auto& timeStats = getCompositionEngine().getTimeStats();
+    if (readyFence.get() < 0) {
+        timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
+    } else {
+        timeStats.recordRenderEngineDuration(renderEngineStart,
+                                             std::make_shared<FenceTime>(
+                                                     new Fence(dup(readyFence.get()))));
+    }
+
+    return readyFence;
+}
+
+std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests(
+        bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
+    std::vector<LayerFE::LayerSettings> clientCompositionLayers;
+    ALOGV("Rendering client layers");
+
+    const auto& outputState = getState();
+    const Region viewportRegion(outputState.viewport);
+    const bool useIdentityTransform = false;
+    bool firstLayer = true;
+    // Used when a layer clears part of the buffer.
+    Region dummyRegion;
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        const auto& layerState = layer->getState();
+        const auto* layerFEState = layer->getLayerFE().getCompositionState();
+        auto& layerFE = layer->getLayerFE();
+
+        const Region clip(viewportRegion.intersect(layerState.visibleRegion));
+        ALOGV("Layer: %s", layerFE.getDebugName());
+        if (clip.isEmpty()) {
+            ALOGV("  Skipping for empty clip");
+            firstLayer = false;
+            continue;
+        }
+
+        const bool clientComposition = layer->requiresClientComposition();
+
+        // We clear the client target for non-client composed layers if
+        // requested by the HWC. We skip this if the layer is not an opaque
+        // rectangle, as by definition the layer must blend with whatever is
+        // underneath. We also skip the first layer as the buffer target is
+        // guaranteed to start out cleared.
+        const bool clearClientComposition =
+                layerState.clearClientTarget && layerFEState->isOpaque && !firstLayer;
+
+        ALOGV("  Composition type: client %d clear %d", clientComposition, clearClientComposition);
+
+        // If the layer casts a shadow but the content casting the shadow is occluded, skip
+        // composing the non-shadow content and only draw the shadows.
+        const bool realContentIsVisible = clientComposition &&
+                !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
+
+        if (clientComposition || clearClientComposition) {
+            compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
+                    clip,
+                    useIdentityTransform,
+                    layer->needsFiltering() || outputState.needsFiltering,
+                    outputState.isSecure,
+                    supportsProtectedContent,
+                    clientComposition ? clearRegion : dummyRegion,
+                    outputState.viewport,
+                    outputDataspace,
+                    realContentIsVisible,
+                    !clientComposition, /* clearContent  */
+            };
+            std::vector<LayerFE::LayerSettings> results =
+                    layerFE.prepareClientCompositionList(targetSettings);
+            if (realContentIsVisible && !results.empty()) {
+                layer->editState().clientCompositionTimestamp = systemTime();
+            }
+
+            clientCompositionLayers.insert(clientCompositionLayers.end(),
+                                           std::make_move_iterator(results.begin()),
+                                           std::make_move_iterator(results.end()));
+            results.clear();
+        }
+
+        firstLayer = false;
+    }
+
+    return clientCompositionLayers;
+}
+
+void Output::appendRegionFlashRequests(
+        const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
+    if (flashRegion.isEmpty()) {
+        return;
+    }
+
+    LayerFE::LayerSettings layerSettings;
+    layerSettings.source.buffer.buffer = nullptr;
+    layerSettings.source.solidColor = half3(1.0, 0.0, 1.0);
+    layerSettings.alpha = half(1.0);
+
+    for (const auto& rect : flashRegion) {
+        layerSettings.geometry.boundaries = rect.toFloatRect();
+        clientCompositionLayers.push_back(layerSettings);
+    }
+}
+
+void Output::setExpensiveRenderingExpected(bool) {
+    // The base class does nothing with this call.
+}
+
+void Output::postFramebuffer() {
+    ATRACE_CALL();
+    ALOGV(__FUNCTION__);
+
+    if (!getState().isEnabled) {
+        return;
+    }
+
+    auto& outputState = editState();
+    outputState.dirtyRegion.clear();
+    mRenderSurface->flip();
+
+    auto frame = presentAndGetFrameFences();
+
+    mRenderSurface->onPresentDisplayCompleted();
+
+    for (auto* layer : getOutputLayersOrderedByZ()) {
+        // 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.
+        sp<Fence> releaseFence = Fence::NO_FENCE;
+
+        if (auto hwcLayer = layer->getHwcLayer()) {
+            if (auto f = frame.layerFences.find(hwcLayer); f != frame.layerFences.end()) {
+                releaseFence = f->second;
+            }
+        }
+
+        // If the layer was client composited in the previous frame, we
+        // need to merge with the previous client target acquire fence.
+        // Since we do not track that, always merge with the current
+        // client target acquire fence when it is available, even though
+        // this is suboptimal.
+        // TODO(b/121291683): Track previous frame client target acquire fence.
+        if (outputState.usesClientComposition) {
+            releaseFence =
+                    Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
+        }
+
+        layer->getLayerFE().onLayerDisplayed(releaseFence);
+    }
+
+    // We've got a list of layers needing fences, that are disjoint with
+    // OutputLayersOrderedByZ.  The best we can do is to
+    // supply them with the present fence.
+    for (auto& weakLayer : mReleasedLayers) {
+        if (auto layer = weakLayer.promote(); layer != nullptr) {
+            layer->onLayerDisplayed(frame.presentFence);
+        }
+    }
+
+    // Clear out the released layers now that we're done with them.
+    mReleasedLayers.clear();
 }
 
 void Output::dirtyEntireOutput() {
-    mState.dirtyRegion.set(mState.bounds);
+    auto& outputState = editState();
+    outputState.dirtyRegion.set(outputState.bounds);
+}
+
+void Output::chooseCompositionStrategy() {
+    // The base output implementation can only do client composition
+    auto& outputState = editState();
+    outputState.usesClientComposition = true;
+    outputState.usesDeviceComposition = false;
+    outputState.reusedClientComposition = false;
+}
+
+bool Output::getSkipColorTransform() const {
+    return true;
+}
+
+compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
+    compositionengine::Output::FrameFences result;
+    if (getState().usesClientComposition) {
+        result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
+    }
+    return result;
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 9549054..ca5be48 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -24,6 +24,11 @@
     dumpVal(out, "isEnabled", isEnabled);
     dumpVal(out, "isSecure", isSecure);
 
+    dumpVal(out, "usesClientComposition", usesClientComposition);
+    dumpVal(out, "usesDeviceComposition", usesDeviceComposition);
+    dumpVal(out, "flipClientTarget", flipClientTarget);
+    dumpVal(out, "reusedClientComposition", reusedClientComposition);
+
     dumpVal(out, "layerStack", layerStackId);
     dumpVal(out, "layerStackInternal", layerStackInternal);
 
@@ -35,7 +40,8 @@
 
     dumpVal(out, "frame", frame);
     dumpVal(out, "viewport", viewport);
-    dumpVal(out, "scissor", scissor);
+    dumpVal(out, "sourceClip", sourceClip);
+    dumpVal(out, "destinationClip", destinationClip);
     dumpVal(out, "needsFiltering", needsFiltering);
 
     out.append("\n   ");
@@ -43,7 +49,8 @@
     dumpVal(out, "colorMode", toString(colorMode), colorMode);
     dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
     dumpVal(out, "dataspace", toString(dataspace), dataspace);
-    dumpVal(out, "colorTransform", colorTransform);
+    dumpVal(out, "colorTransformMatrix", colorTransformMatrix);
+    dumpVal(out, "target dataspace", toString(targetDataspace), targetDataspace);
 
     out.append("\n");
 }
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 5ce72b0..3aa7956 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -15,17 +15,23 @@
  */
 
 #include <android-base/stringprintf.h>
-#include <compositionengine/CompositionEngine.h>
-#include <compositionengine/Layer.h>
+#include <compositionengine/DisplayColorProfile.h>
 #include <compositionengine/LayerFE.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/Output.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine {
 
 OutputLayer::~OutputLayer() = default;
@@ -44,56 +50,24 @@
 
 } // namespace
 
-std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(
-        const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId,
-        const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer,
-        sp<compositionengine::LayerFE> layerFE) {
-    auto result = std::make_unique<OutputLayer>(output, layer, layerFE);
-    result->initialize(compositionEngine, displayId);
-    return result;
+std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output& output,
+                                               const sp<compositionengine::LayerFE>& layerFE) {
+    return createOutputLayerTemplated<OutputLayer>(output, layerFE);
 }
 
-OutputLayer::OutputLayer(const Output& output, std::shared_ptr<Layer> layer, sp<LayerFE> layerFE)
-      : mOutput(output), mLayer(layer), mLayerFE(layerFE) {}
-
 OutputLayer::~OutputLayer() = default;
 
-void OutputLayer::initialize(const CompositionEngine& compositionEngine,
-                             std::optional<DisplayId> displayId) {
-    if (!displayId) {
-        return;
+void OutputLayer::setHwcLayer(std::shared_ptr<HWC2::Layer> hwcLayer) {
+    auto& state = editState();
+    if (hwcLayer) {
+        state.hwc.emplace(std::move(hwcLayer));
+    } else {
+        state.hwc.reset();
     }
-
-    auto& hwc = compositionEngine.getHwComposer();
-
-    mState.hwc.emplace(std::shared_ptr<HWC2::Layer>(hwc.createLayer(*displayId),
-                                                    [&hwc, displayId](HWC2::Layer* layer) {
-                                                        hwc.destroyLayer(*displayId, layer);
-                                                    }));
-}
-
-const compositionengine::Output& OutputLayer::getOutput() const {
-    return mOutput;
-}
-
-compositionengine::Layer& OutputLayer::getLayer() const {
-    return *mLayer;
-}
-
-compositionengine::LayerFE& OutputLayer::getLayerFE() const {
-    return *mLayerFE;
-}
-
-const OutputLayerCompositionState& OutputLayer::getState() const {
-    return mState;
-}
-
-OutputLayerCompositionState& OutputLayer::editState() {
-    return mState;
 }
 
 Rect OutputLayer::calculateInitialCrop() const {
-    const auto& layerState = mLayer->getState().frontEnd;
+    const auto& layerState = *getLayerFE().getCompositionState();
 
     // apply the projection's clipping to the window crop in
     // layerstack space, and convert-back to layer space.
@@ -101,9 +75,9 @@
     // pixels in the buffer.
 
     FloatRect activeCropFloat =
-            reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion);
+            reduce(layerState.geomLayerBounds, layerState.transparentRegionHint);
 
-    const Rect& viewport = mOutput.getState().viewport;
+    const Rect& viewport = getOutput().getState().viewport;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     // Transform to screen space.
@@ -126,8 +100,8 @@
 }
 
 FloatRect OutputLayer::calculateOutputSourceCrop() const {
-    const auto& layerState = mLayer->getState().frontEnd;
-    const auto& outputState = mOutput.getState();
+    const auto& layerState = *getLayerFE().getCompositionState();
+    const auto& outputState = getOutput().getState();
 
     if (!layerState.geomUsesSourceCrop) {
         return {};
@@ -203,12 +177,12 @@
 }
 
 Rect OutputLayer::calculateOutputDisplayFrame() const {
-    const auto& layerState = mLayer->getState().frontEnd;
-    const auto& outputState = mOutput.getState();
+    const auto& layerState = *getLayerFE().getCompositionState();
+    const auto& outputState = getOutput().getState();
 
     // 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 = layerState.geomActiveTransparentRegion;
+    Region activeTransparentRegion = layerState.transparentRegionHint;
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
     const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
     const Rect& bufferSize = layerState.geomBufferSize;
@@ -250,8 +224,8 @@
 }
 
 uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const {
-    const auto& layerState = mLayer->getState().frontEnd;
-    const auto& outputState = mOutput.getState();
+    const auto& layerState = *getLayerFE().getCompositionState();
+    const auto& outputState = getOutput().getState();
 
     /*
      * Transformations are applied in this order:
@@ -261,7 +235,7 @@
      * (NOTE: the matrices are multiplied in reverse order)
      */
     const ui::Transform& layerTransform = layerState.geomLayerTransform;
-    const ui::Transform displayTransform{outputState.orientation};
+    const ui::Transform displayTransform{outputState.transform};
     const ui::Transform bufferTransform{layerState.geomBufferTransform};
     ui::Transform transform(displayTransform * layerTransform * bufferTransform);
 
@@ -289,99 +263,408 @@
     return transform.getOrientation();
 } // namespace impl
 
-void OutputLayer::updateCompositionState(bool includeGeometry) {
+void OutputLayer::updateCompositionState(bool includeGeometry, bool forceClientComposition) {
+    const auto* layerFEState = getLayerFE().getCompositionState();
+    if (!layerFEState) {
+        return;
+    }
+
+    const auto& outputState = getOutput().getState();
+    const auto& profile = *getOutput().getDisplayColorProfile();
+    auto& state = editState();
+
     if (includeGeometry) {
-        mState.displayFrame = calculateOutputDisplayFrame();
-        mState.sourceCrop = calculateOutputSourceCrop();
-        mState.bufferTransform =
+        // Clear the forceClientComposition flag before it is set for any
+        // reason. Note that since it can be set by some checks below when
+        // updating the geometry state, we only clear it when updating the
+        // geometry since those conditions for forcing client composition won't
+        // go away otherwise.
+        state.forceClientComposition = false;
+
+        state.displayFrame = calculateOutputDisplayFrame();
+        state.sourceCrop = calculateOutputSourceCrop();
+        state.bufferTransform =
                 static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform());
 
-        if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) ||
-            (mState.bufferTransform & ui::Transform::ROT_INVALID)) {
-            mState.forceClientComposition = true;
+        if ((layerFEState->isSecure && !outputState.isSecure) ||
+            (state.bufferTransform & ui::Transform::ROT_INVALID)) {
+            state.forceClientComposition = true;
+        }
+    }
+
+    // Determine the output dependent dataspace for this layer. If it is
+    // colorspace agnostic, it just uses the dataspace chosen for the output to
+    // avoid the need for color conversion.
+    state.dataspace = layerFEState->isColorspaceAgnostic &&
+                    outputState.targetDataspace != ui::Dataspace::UNKNOWN
+            ? outputState.targetDataspace
+            : layerFEState->dataspace;
+
+    // These are evaluated every frame as they can potentially change at any
+    // time.
+    if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
+        forceClientComposition) {
+        state.forceClientComposition = true;
+    }
+}
+
+void OutputLayer::writeStateToHWC(bool includeGeometry) {
+    const auto& state = getState();
+    // Skip doing this if there is no HWC interface
+    if (!state.hwc) {
+        return;
+    }
+
+    auto& hwcLayer = (*state.hwc).hwcLayer;
+    if (!hwcLayer) {
+        ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s",
+              getLayerFE().getDebugName(), getOutput().getName().c_str());
+        return;
+    }
+
+    const auto* outputIndependentState = getLayerFE().getCompositionState();
+    if (!outputIndependentState) {
+        return;
+    }
+
+    auto requestedCompositionType = outputIndependentState->compositionType;
+
+    if (includeGeometry) {
+        writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType);
+        writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), *outputIndependentState);
+    }
+
+    writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
+    writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState);
+
+    writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType);
+
+    // Always set the layer color after setting the composition type.
+    writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+}
+
+void OutputLayer::writeOutputDependentGeometryStateToHWC(
+        HWC2::Layer* hwcLayer, Hwc2::IComposerClient::Composition requestedCompositionType) {
+    const auto& outputDependentState = getState();
+
+    if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
+              getLayerFE().getDebugName(), outputDependentState.displayFrame.left,
+              outputDependentState.displayFrame.top, outputDependentState.displayFrame.right,
+              outputDependentState.displayFrame.bottom, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
+              "%s (%d)",
+              getLayerFE().getDebugName(), outputDependentState.sourceCrop.left,
+              outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right,
+              outputDependentState.sourceCrop.bottom, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(),
+              outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    // Solid-color layers should always use an identity transform.
+    const auto bufferTransform =
+            requestedCompositionType != Hwc2::IComposerClient::Composition::SOLID_COLOR
+            ? outputDependentState.bufferTransform
+            : static_cast<Hwc2::Transform>(0);
+    if (auto error = hwcLayer->setTransform(static_cast<HWC2::Transform>(bufferTransform));
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set transform %s: %s (%d)", getLayerFE().getDebugName(),
+              toString(outputDependentState.bufferTransform).c_str(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeOutputIndependentGeometryStateToHWC(
+        HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+    if (auto error = hwcLayer->setBlendMode(
+                static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(),
+              toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(),
+              outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setInfo(static_cast<uint32_t>(outputIndependentState.type),
+                                       static_cast<uint32_t>(outputIndependentState.appId));
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    for (const auto& [name, entry] : outputIndependentState.metadata) {
+        if (auto error = hwcLayer->setLayerGenericMetadata(name, entry.mandatory, entry.value);
+            error != HWC2::Error::None) {
+            ALOGE("[%s] Failed to set generic metadata %s %s (%d)", getLayerFE().getDebugName(),
+                  name.c_str(), to_string(error).c_str(), static_cast<int32_t>(error));
         }
     }
 }
 
-void OutputLayer::writeStateToHWC(bool includeGeometry) const {
+void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) {
+    const auto& outputDependentState = getState();
+
+    // TODO(lpique): b/121291683 outputSpaceVisibleRegion is output-dependent geometry
+    // state and should not change every frame.
+    if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG);
+    }
+
+    if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(),
+              outputDependentState.dataspace, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
+        HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) {
+    switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
+        case HWC2::Error::None:
+            break;
+        case HWC2::Error::Unsupported:
+            editState().forceClientComposition = true;
+            break;
+        default:
+            ALOGE("[%s] Failed to set color transform: %s (%d)", getLayerFE().getDebugName(),
+                  to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+        outputIndependentState.surfaceDamage.dump(LOG_TAG);
+    }
+
+    // Content-specific per-frame state
+    switch (outputIndependentState.compositionType) {
+        case Hwc2::IComposerClient::Composition::SOLID_COLOR:
+            // For compatibility, should be written AFTER the composition type.
+            break;
+        case Hwc2::IComposerClient::Composition::SIDEBAND:
+            writeSidebandStateToHWC(hwcLayer, outputIndependentState);
+            break;
+        case Hwc2::IComposerClient::Composition::CURSOR:
+        case Hwc2::IComposerClient::Composition::DEVICE:
+            writeBufferStateToHWC(hwcLayer, outputIndependentState);
+            break;
+        case Hwc2::IComposerClient::Composition::INVALID:
+        case Hwc2::IComposerClient::Composition::CLIENT:
+            // Ignored
+            break;
+    }
+}
+
+void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
+                                            const LayerFECompositionState& outputIndependentState) {
+    if (outputIndependentState.compositionType != Hwc2::IComposerClient::Composition::SOLID_COLOR) {
+        return;
+    }
+
+    hwc_color_t color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
+                         static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
+                         static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
+                         255};
+
+    if (auto error = hwcLayer->setColor(color); error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set color: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeSidebandStateToHWC(HWC2::Layer* hwcLayer,
+                                          const LayerFECompositionState& outputIndependentState) {
+    if (auto error = hwcLayer->setSidebandStream(outputIndependentState.sidebandStream->handle());
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", getLayerFE().getDebugName(),
+              outputIndependentState.sidebandStream->handle(), to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer,
+                                        const LayerFECompositionState& outputIndependentState) {
+    auto supportedPerFrameMetadata =
+            getOutput().getDisplayColorProfile()->getSupportedPerFrameMetadata();
+    if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata,
+                                                   outputIndependentState.hdrMetadata);
+        error != HWC2::Error::None && error != HWC2::Error::Unsupported) {
+        ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", getLayerFE().getDebugName(),
+              to_string(error).c_str(), static_cast<int32_t>(error));
+    }
+
+    uint32_t hwcSlot = 0;
+    sp<GraphicBuffer> hwcBuffer;
+    // We need access to the output-dependent state for the buffer cache there,
+    // though otherwise the buffer is not output-dependent.
+    editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot,
+                                                 outputIndependentState.buffer, &hwcSlot,
+                                                 &hwcBuffer);
+
+    if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(),
+              outputIndependentState.buffer->handle, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+void OutputLayer::writeCompositionTypeToHWC(
+        HWC2::Layer* hwcLayer, Hwc2::IComposerClient::Composition requestedCompositionType) {
+    auto& outputDependentState = editState();
+
+    // If we are forcing client composition, we need to tell the HWC
+    if (outputDependentState.forceClientComposition) {
+        requestedCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+    }
+
+    // Set the requested composition type with the HWC whenever it changes
+    if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) {
+        outputDependentState.hwc->hwcCompositionType = requestedCompositionType;
+
+        if (auto error = hwcLayer->setCompositionType(
+                    static_cast<HWC2::Composition>(requestedCompositionType));
+            error != HWC2::Error::None) {
+            ALOGE("[%s] Failed to set composition type %s: %s (%d)", getLayerFE().getDebugName(),
+                  toString(requestedCompositionType).c_str(), to_string(error).c_str(),
+                  static_cast<int32_t>(error));
+        }
+    }
+}
+
+void OutputLayer::writeCursorPositionToHWC() const {
     // Skip doing this if there is no HWC interface
-    if (!mState.hwc) {
-        return;
-    }
-
-    auto& hwcLayer = (*mState.hwc).hwcLayer;
+    auto hwcLayer = getHwcLayer();
     if (!hwcLayer) {
-        ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s",
-              mLayerFE->getDebugName(), mOutput.getName().c_str());
         return;
     }
 
-    if (includeGeometry) {
-        // Output dependent state
-
-        if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)",
-                  mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top,
-                  mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: "
-                  "%s (%d)",
-                  mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top,
-                  mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z,
-                  to_string(error).c_str(), static_cast<int32_t>(error));
-        }
-
-        if (auto error =
-                    hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform));
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(),
-                  toString(mState.bufferTransform).c_str(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        // Output independent state
-
-        const auto& outputIndependentState = mLayer->getState().frontEnd;
-
-        if (auto error = hwcLayer->setBlendMode(
-                    static_cast<HWC2::BlendMode>(outputIndependentState.blendMode));
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(),
-                  toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(),
-                  outputIndependentState.alpha, to_string(error).c_str(),
-                  static_cast<int32_t>(error));
-        }
-
-        if (auto error =
-                    hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId);
-            error != HWC2::Error::None) {
-            ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(),
-                  to_string(error).c_str(), static_cast<int32_t>(error));
-        }
+    const auto* layerFEState = getLayerFE().getCompositionState();
+    if (!layerFEState) {
+        return;
     }
+
+    const auto& outputState = getOutput().getState();
+
+    Rect frame = layerFEState->cursorFrame;
+    frame.intersect(outputState.viewport, &frame);
+    Rect position = outputState.transform.transform(frame);
+
+    if (auto error = hwcLayer->setCursorPosition(position.left, position.top);
+        error != HWC2::Error::None) {
+        ALOGE("[%s] Failed to set cursor position to (%d, %d): %s (%d)",
+              getLayerFE().getDebugName(), position.left, position.top, to_string(error).c_str(),
+              static_cast<int32_t>(error));
+    }
+}
+
+HWC2::Layer* OutputLayer::getHwcLayer() const {
+    const auto& state = getState();
+    return state.hwc ? state.hwc->hwcLayer.get() : nullptr;
+}
+
+bool OutputLayer::requiresClientComposition() const {
+    const auto& state = getState();
+    return !state.hwc ||
+            state.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
+}
+
+bool OutputLayer::isHardwareCursor() const {
+    const auto& state = getState();
+    return state.hwc && state.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CURSOR;
+}
+
+void OutputLayer::detectDisallowedCompositionTypeChange(
+        Hwc2::IComposerClient::Composition from, Hwc2::IComposerClient::Composition to) const {
+    bool result = false;
+    switch (from) {
+        case Hwc2::IComposerClient::Composition::INVALID:
+        case Hwc2::IComposerClient::Composition::CLIENT:
+            result = false;
+            break;
+
+        case Hwc2::IComposerClient::Composition::DEVICE:
+        case Hwc2::IComposerClient::Composition::SOLID_COLOR:
+            result = (to == Hwc2::IComposerClient::Composition::CLIENT);
+            break;
+
+        case Hwc2::IComposerClient::Composition::CURSOR:
+        case Hwc2::IComposerClient::Composition::SIDEBAND:
+            result = (to == Hwc2::IComposerClient::Composition::CLIENT ||
+                      to == Hwc2::IComposerClient::Composition::DEVICE);
+            break;
+    }
+
+    if (!result) {
+        ALOGE("[%s] Invalid device requested composition type change: %s (%d) --> %s (%d)",
+              getLayerFE().getDebugName(), toString(from).c_str(), static_cast<int>(from),
+              toString(to).c_str(), static_cast<int>(to));
+    }
+}
+
+void OutputLayer::applyDeviceCompositionTypeChange(
+        Hwc2::IComposerClient::Composition compositionType) {
+    auto& state = editState();
+    LOG_FATAL_IF(!state.hwc);
+    auto& hwcState = *state.hwc;
+
+    detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType);
+
+    hwcState.hwcCompositionType = compositionType;
+}
+
+void OutputLayer::prepareForDeviceLayerRequests() {
+    auto& state = editState();
+    state.clearClientTarget = false;
+}
+
+void OutputLayer::applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) {
+    auto& state = editState();
+    switch (request) {
+        case Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET:
+            state.clearClientTarget = true;
+            break;
+
+        default:
+            ALOGE("[%s] Unknown device layer request %s (%d)", getLayerFE().getDebugName(),
+                  toString(request).c_str(), static_cast<int>(request));
+            break;
+    }
+}
+
+bool OutputLayer::needsFiltering() const {
+    const auto& state = getState();
+    const auto& displayFrame = state.displayFrame;
+    const auto& sourceCrop = state.sourceCrop;
+    return sourceCrop.getHeight() != displayFrame.getHeight() ||
+            sourceCrop.getWidth() != displayFrame.getWidth();
 }
 
 void OutputLayer::dump(std::string& out) const {
     using android::base::StringAppendF;
 
-    StringAppendF(&out, "  - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(),
-                  mLayerFE->getDebugName());
-    mState.dump(out);
+    StringAppendF(&out, "  - Output Layer %p(%s)\n", this, getLayerFE().getDebugName());
+    dumpState(out);
 }
 
 } // namespace impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index 861ea57..165e320 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -17,8 +17,15 @@
 #include <compositionengine/impl/DumpHelpers.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWC2.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine::impl {
 
 namespace {
@@ -42,11 +49,24 @@
     dumpVal(out, "visibleRegion", visibleRegion);
 
     out.append("      ");
+    dumpVal(out, "visibleNonTransparentRegion", visibleNonTransparentRegion);
+
+    out.append("      ");
+    dumpVal(out, "coveredRegion", coveredRegion);
+
+    out.append("      ");
+    dumpVal(out, "output visibleRegion", outputSpaceVisibleRegion);
+
+    out.append("      ");
+    dumpVal(out, "shadowRegion", shadowRegion);
+
+    out.append("      ");
     dumpVal(out, "forceClientComposition", forceClientComposition);
     dumpVal(out, "clearClientTarget", clearClientTarget);
     dumpVal(out, "displayFrame", displayFrame);
     dumpVal(out, "sourceCrop", sourceCrop);
     dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform);
+    dumpVal(out, "dataspace", toString(dataspace), dataspace);
     dumpVal(out, "z-index", z);
 
     if (hwc) {
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 3fcd9d1..660baff 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -23,7 +23,9 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/RenderSurfaceCreationArgs.h>
 #include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/RenderSurface.h>
+
 #include <log/log.h>
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
@@ -32,8 +34,15 @@
 #include <ui/Rect.h>
 #include <utils/Trace.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android::compositionengine {
 
 RenderSurface::~RenderSurface() = default;
@@ -42,12 +51,13 @@
 
 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));
+        compositionengine::Display& display,
+        const compositionengine::RenderSurfaceCreationArgs& args) {
+    return std::make_unique<RenderSurface>(compositionEngine, display, args);
 }
 
 RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display,
-                             RenderSurfaceCreationArgs&& args)
+                             const RenderSurfaceCreationArgs& args)
       : mCompositionEngine(compositionEngine),
         mDisplay(display),
         mNativeWindow(args.nativeWindow),
@@ -85,7 +95,8 @@
 }
 
 void RenderSurface::setDisplaySize(const ui::Size& size) {
-    mDisplaySurface->resizeBuffers(size.width, size.height);
+    mDisplaySurface->resizeBuffers(static_cast<uint32_t>(size.width),
+                                   static_cast<uint32_t>(size.height));
     mSize = size;
 }
 
@@ -110,32 +121,25 @@
     return mDisplaySurface->beginFrame(mustRecompose);
 }
 
-status_t RenderSurface::prepareFrame() {
-    auto& hwc = mCompositionEngine.getHwComposer();
-    const auto id = mDisplay.getId();
-    if (id) {
-        status_t error = hwc.prepare(*id, mDisplay);
-        if (error != NO_ERROR) {
-            return error;
-        }
-    }
-
+void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComposition) {
     DisplaySurface::CompositionType compositionType;
-    const bool hasClient = hwc.hasClientComposition(id);
-    const bool hasDevice = hwc.hasDeviceComposition(id);
-    if (hasClient && hasDevice) {
+    if (usesClientComposition && usesDeviceComposition) {
         compositionType = DisplaySurface::COMPOSITION_MIXED;
-    } else if (hasClient) {
-        compositionType = DisplaySurface::COMPOSITION_GLES;
-    } else if (hasDevice) {
+    } else if (usesClientComposition) {
+        compositionType = DisplaySurface::COMPOSITION_GPU;
+    } else if (usesDeviceComposition) {
         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
+        // this. Call it a HWC frame since we won't be doing any GPU work but
         // will do a prepare/set cycle.
         compositionType = DisplaySurface::COMPOSITION_HWC;
     }
-    return mDisplaySurface->prepareFrame(compositionType);
+
+    if (status_t result = mDisplaySurface->prepareFrame(compositionType); result != NO_ERROR) {
+        ALOGE("updateCompositionType failed for %s: %d (%s)", mDisplay.getName().c_str(), result,
+              strerror(-result));
+    }
 }
 
 sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) {
@@ -162,11 +166,10 @@
     return mGraphicBuffer;
 }
 
-void RenderSurface::queueBuffer(base::unique_fd&& readyFence) {
-    auto& hwc = mCompositionEngine.getHwComposer();
-    const auto id = mDisplay.getId();
+void RenderSurface::queueBuffer(base::unique_fd readyFence) {
+    auto& state = mDisplay.getState();
 
-    if (hwc.hasClientComposition(id) || hwc.hasFlipClientTargetRequest(id)) {
+    if (state.usesClientComposition || state.flipClientTarget) {
         // hasFlipClientTargetRequest could return true even if we haven't
         // dequeued a buffer before. Try dequeueing one if we don't have a
         // buffer ready.
@@ -215,13 +218,6 @@
     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++;
 }
diff --git a/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
new file mode 100644
index 0000000..2675dcf
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
@@ -0,0 +1,126 @@
+/*
+ * 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
+
+/**
+ * CallOrderStateMachineHelper is a helper class for setting up a compile-time
+ * checked state machine that a sequence of calls is correct for completely
+ * setting up the state for some other type.
+ *
+ * Two examples where this could be used are with setting up a "Builder" flow
+ * for initializing an instance of some type, and writing tests where the state
+ * machine sets up expectations and preconditions, calls the function under
+ * test, and then evaluations postconditions.
+ *
+ * The purpose of this helper is to offload some of the boilerplate code to
+ * simplify the actual state classes, and is also a place to document how to
+ * go about setting up the state classes.
+ *
+ * To work at compile time, the idea is that each state is a unique C++ type,
+ * and the valid transitions between states are given by member functions on
+ * those types, with those functions returning a simple value type expressing
+ * the new state to use. Illegal state transitions become a compile error because
+ * a named member function does not exist.
+ *
+ * Example usage in a test:
+ *
+ *    A two step (+ terminator step) setup process can defined using:
+ *
+ *        class Step1 : public CallOrderStateMachineHelper<TestFixtureType, Step1> {
+ *           [[nodiscard]] auto firstMockCalledWith(int value1) {
+ *              // Set up an expectation or initial state using the fixture
+ *              EXPECT_CALL(getInstance->firstMock, FirstCall(value1));
+ *              return nextState<Step2>();
+ *           }
+ *        };
+ *
+ *        class Step2 : public CallOrderStateMachineHelper<TestFixtureType, Step2> {
+ *           [[nodiscard]] auto secondMockCalledWith(int value2) {
+ *              // Set up an expectation or initial state using the fixture
+ *              EXPECT_CALL(getInstance()->secondMock, SecondCall(value2));
+ *              return nextState<StepExecute>();
+ *           }
+ *        };
+ *
+ *        class StepExecute : public CallOrderStateMachineHelper<TestFixtureType, Step3> {
+ *           void execute() {
+ *              invokeFunctionUnderTest();
+ *           }
+ *        };
+ *
+ *    Note how the non-terminator steps return by value and use [[nodiscard]] to
+ *    enforce the setup flow. Only the terminator step returns void.
+ *
+ *    This can then be used in the tests with:
+ *
+ *        Step1::make(this).firstMockCalledWith(value1)
+ *                .secondMockCalledWith(value2)
+ *                .execute);
+ *
+ *    If the test fixture defines a `verify()` helper function which returns
+ *    `Step1::make(this)`, this can be simplified to:
+ *
+ *        verify().firstMockCalledWith(value1)
+ *                .secondMockCalledWith(value2)
+ *                .execute();
+ *
+ *    This is equivalent to the following calls made by the text function:
+ *
+ *        EXPECT_CALL(firstMock, FirstCall(value1));
+ *        EXPECT_CALL(secondMock, SecondCall(value2));
+ *        invokeFunctionUnderTest();
+ */
+template <typename InstanceType, typename CurrentStateType>
+class CallOrderStateMachineHelper {
+public:
+    CallOrderStateMachineHelper() = default;
+
+    // Disallow copying
+    CallOrderStateMachineHelper(const CallOrderStateMachineHelper&) = delete;
+    CallOrderStateMachineHelper& operator=(const CallOrderStateMachineHelper&) = delete;
+
+    // Moving is intended use case.
+    CallOrderStateMachineHelper(CallOrderStateMachineHelper&&) = default;
+    CallOrderStateMachineHelper& operator=(CallOrderStateMachineHelper&&) = default;
+
+    // Using a static "Make" function means the CurrentStateType classes do not
+    // need anything other than a default no-argument constructor.
+    static CurrentStateType make(InstanceType* instance) {
+        auto helper = CurrentStateType();
+        helper.mInstance = instance;
+        return helper;
+    }
+
+    // Each non-terminal state function
+    template <typename NextStateType>
+    auto nextState() {
+        // Note: Further operations on the current state become undefined
+        // operations as the instance pointer is moved to the next state type.
+        // But that doesn't stop someone from storing an intermediate state
+        // instance as a local and possibly calling one than one member function
+        // on it. By swapping with nullptr, we at least can try to catch this
+        // this at runtime.
+        InstanceType* instance = nullptr;
+        std::swap(instance, mInstance);
+        return NextStateType::make(instance);
+    }
+
+    InstanceType* getInstance() const { return mInstance; }
+
+private:
+    InstanceType* mInstance;
+};
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 3766f27..d889d74 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -14,27 +14,42 @@
  * limitations under the License.
  */
 
+#include <compositionengine/CompositionRefreshArgs.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/mock/LayerFE.h>
+#include <compositionengine/mock/Output.h>
+#include <compositionengine/mock/OutputLayer.h>
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
 #include "MockHWComposer.h"
+#include "TimeStats/TimeStats.h"
 
 namespace android::compositionengine {
 namespace {
 
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Ref;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
 using ::testing::StrictMock;
 
-class CompositionEngineTest : public testing::Test {
-public:
-    ~CompositionEngineTest() override;
-    mock::HWComposer* mHwc = new StrictMock<mock::HWComposer>();
+struct CompositionEngineTest : public testing::Test {
+    android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
     renderengine::mock::RenderEngine* mRenderEngine =
             new StrictMock<renderengine::mock::RenderEngine>();
-    impl::CompositionEngine mEngine;
-};
+    std::shared_ptr<TimeStats> mTimeStats;
 
-CompositionEngineTest::~CompositionEngineTest() = default;
+    impl::CompositionEngine mEngine;
+    CompositionRefreshArgs mRefreshArgs;
+
+    std::shared_ptr<mock::Output> mOutput1{std::make_shared<StrictMock<mock::Output>>()};
+    std::shared_ptr<mock::Output> mOutput2{std::make_shared<StrictMock<mock::Output>>()};
+    std::shared_ptr<mock::Output> mOutput3{std::make_shared<StrictMock<mock::Output>>()};
+};
 
 TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) {
     auto engine = impl::createCompositionEngine();
@@ -53,5 +68,203 @@
     EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
 }
 
+TEST_F(CompositionEngineTest, canSetTimeStats) {
+    mEngine.setTimeStats(mTimeStats);
+
+    EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats());
+}
+
+/*
+ * CompositionEngine::present
+ */
+
+struct CompositionEnginePresentTest : public CompositionEngineTest {
+    struct CompositionEnginePartialMock : public impl::CompositionEngine {
+        // These are the overridable functions CompositionEngine::present() may
+        // call, and have separate test coverage.
+        MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+    };
+
+    StrictMock<CompositionEnginePartialMock> mEngine;
+};
+
+TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
+    // present() always calls preComposition()
+    EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+    mEngine.present(mRefreshArgs);
+}
+
+TEST_F(CompositionEnginePresentTest, worksAsExpected) {
+    // Expect calls to in a certain sequence
+    InSequence seq;
+
+    // present() always calls preComposition()
+    EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+
+    // The first step in presenting is to make sure all outputs are prepared.
+    EXPECT_CALL(*mOutput1, prepare(Ref(mRefreshArgs), _));
+    EXPECT_CALL(*mOutput2, prepare(Ref(mRefreshArgs), _));
+    EXPECT_CALL(*mOutput3, prepare(Ref(mRefreshArgs), _));
+
+    // The next step in presenting is to make sure all outputs have the latest
+    // state from the front-end (SurfaceFlinger).
+    EXPECT_CALL(*mOutput1, updateLayerStateFromFE(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput2, updateLayerStateFromFE(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput3, updateLayerStateFromFE(Ref(mRefreshArgs)));
+
+    // The last step is to actually present each output.
+    EXPECT_CALL(*mOutput1, present(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput2, present(Ref(mRefreshArgs)));
+    EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)));
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+    mEngine.present(mRefreshArgs);
+}
+
+/*
+ * CompositionEngine::updateCursorAsync
+ */
+
+struct CompositionEngineUpdateCursorAsyncTest : public CompositionEngineTest {
+public:
+    struct Layer {
+        Layer() { EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE)); }
+
+        StrictMock<mock::OutputLayer> outputLayer;
+        StrictMock<mock::LayerFE> layerFE;
+        LayerFECompositionState layerFEState;
+    };
+
+    CompositionEngineUpdateCursorAsyncTest() {
+        EXPECT_CALL(*mOutput1, getOutputLayerCount()).WillRepeatedly(Return(0u));
+        EXPECT_CALL(*mOutput1, getOutputLayerOrderedByZByIndex(_)).Times(0);
+
+        EXPECT_CALL(*mOutput2, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(*mOutput2, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mOutput2Layer1.outputLayer));
+
+        EXPECT_CALL(*mOutput3, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mOutput3Layer1.outputLayer));
+        EXPECT_CALL(*mOutput3, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mOutput3Layer2.outputLayer));
+    }
+
+    Layer mOutput2Layer1;
+    Layer mOutput3Layer1;
+    Layer mOutput3Layer2;
+};
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoOutputs) {
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesNoLayersBeingCursorLayers) {
+    EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(false));
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesMultipleLayersBeingCursorLayers) {
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput2Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput2Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(mOutput2Layer1.outputLayer, writeCursorPositionToHWC());
+    }
+
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput3Layer1.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput3Layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(mOutput3Layer1.outputLayer, writeCursorPositionToHWC());
+    }
+
+    {
+        InSequence seq;
+        EXPECT_CALL(mOutput3Layer2.outputLayer, isHardwareCursor()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput3Layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::Cursor));
+        EXPECT_CALL(mOutput3Layer2.outputLayer, writeCursorPositionToHWC());
+    }
+
+    mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
+
+    mEngine.updateCursorAsync(mRefreshArgs);
+}
+
+/*
+ * CompositionEngine::preComposition
+ */
+
+struct CompositionTestPreComposition : public CompositionEngineTest {
+    sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()};
+};
+
+TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) {
+    const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC);
+    mEngine.preComposition(mRefreshArgs);
+    const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC);
+
+    // The frame timestamp should be between the before and after timestamps
+    EXPECT_GE(mEngine.getLastFrameRefreshTimestamp(), before);
+    EXPECT_LE(mEngine.getLastFrameRefreshTimestamp(), after);
+}
+
+TEST_F(CompositionTestPreComposition, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) {
+    nsecs_t ts1 = 0;
+    nsecs_t ts2 = 0;
+    nsecs_t ts3 = 0;
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+    mEngine.preComposition(mRefreshArgs);
+
+    // Each of the onPreComposition calls should used the same refresh timestamp
+    EXPECT_EQ(ts1, mEngine.getLastFrameRefreshTimestamp());
+    EXPECT_EQ(ts2, mEngine.getLastFrameRefreshTimestamp());
+    EXPECT_EQ(ts3, mEngine.getLastFrameRefreshTimestamp());
+}
+
+TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+
+    mEngine.setNeedsAnotherUpdateForTest(true);
+
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+    mEngine.preComposition(mRefreshArgs);
+
+    // The call should have cleared the needsAnotherUpdate flag
+    EXPECT_FALSE(mEngine.needsAnotherUpdate());
+}
+
+TEST_F(CompositionTestPreComposition,
+       preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
+    EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
+
+    mRefreshArgs.outputs = {mOutput1};
+    mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+    mEngine.preComposition(mRefreshArgs);
+
+    EXPECT_TRUE(mEngine.needsAnotherUpdate());
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index 9215884..21b9aa9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -19,35 +19,6 @@
 #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 {
 
@@ -638,5 +609,66 @@
     checkGetBestColorMode(profile, expectedResults);
 }
 
+/*
+ * RenderSurface::isDataspaceSupported()
+ */
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithNoHdrSupport) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHdr10Support) {
+    auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHlgSupport) {
+    auto profile = ProfileFactory::createProfileWithBT2100PQSupport();
+
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ));
+    EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_HLG));
+    EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG));
+}
+
+/*
+ * RenderSurface::getTargetDataspace()
+ */
+
+TEST_F(DisplayColorProfileTest, getTargetDataspaceWorks) {
+    auto profile = ProfileFactory::createProfileWithNoColorModeSupport();
+
+    // For a non-HDR colorspace with no colorSpaceAgnosticDataspace override,
+    // the input dataspace should be returned.
+    EXPECT_EQ(Dataspace::DISPLAY_P3,
+              profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
+                                         Dataspace::UNKNOWN));
+
+    // If colorSpaceAgnosticDataspace is set, its value should be returned
+    EXPECT_EQ(Dataspace::V0_SRGB,
+              profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3,
+                                         Dataspace::V0_SRGB));
+
+    // For an HDR colorspace, Dataspace::UNKNOWN should be returned.
+    EXPECT_EQ(Dataspace::UNKNOWN,
+              profile.getTargetDataspace(ColorMode::BT2100_PQ, Dataspace::BT2020_PQ,
+                                         Dataspace::UNKNOWN));
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 33444a5..16f7a4e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -21,34 +21,102 @@
 #include <compositionengine/DisplaySurface.h>
 #include <compositionengine/RenderSurfaceCreationArgs.h>
 #include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/RenderSurface.h>
 #include <compositionengine/mock/CompositionEngine.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/NativeWindow.h>
+#include <compositionengine/mock/OutputLayer.h>
 #include <compositionengine/mock/RenderSurface.h>
 #include <gtest/gtest.h>
 
+#include "MockHWC2.h"
 #include "MockHWComposer.h"
+#include "MockPowerAdvisor.h"
 
 namespace android::compositionengine {
 namespace {
 
+using testing::_;
+using testing::DoAll;
+using testing::InSequence;
+using testing::NiceMock;
 using testing::Return;
 using testing::ReturnRef;
+using testing::Sequence;
+using testing::SetArgPointee;
 using testing::StrictMock;
 
 constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
 
-class DisplayTest : public testing::Test {
-public:
-    ~DisplayTest() override = default;
+struct Layer {
+    Layer() {
+        EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
+        EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
+    }
 
-    StrictMock<android::mock::HWComposer> mHwComposer;
-    StrictMock<mock::CompositionEngine> mCompositionEngine;
-    sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
-    impl::Display mDisplay{mCompositionEngine,
-                           DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
+    sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+    StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
+    StrictMock<HWC2::mock::Layer> hwc2Layer;
 };
 
-/* ------------------------------------------------------------------------
+struct LayerNoHWC2Layer {
+    LayerNoHWC2Layer() {
+        EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE));
+        EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(nullptr));
+    }
+
+    sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+    StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
+};
+
+struct DisplayTest : public testing::Test {
+    class Display : public impl::Display {
+    public:
+        explicit Display(const compositionengine::DisplayCreationArgs& args)
+              : impl::Display(args) {}
+
+        using impl::Display::injectOutputLayerForTest;
+        virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+    };
+
+    static std::shared_ptr<Display> createDisplay(
+            const compositionengine::CompositionEngine& compositionEngine,
+            compositionengine::DisplayCreationArgs&& args) {
+        return impl::createDisplayTemplated<Display>(compositionEngine, args);
+    }
+
+    DisplayTest() {
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+        mDisplay->injectOutputLayerForTest(
+                std::unique_ptr<compositionengine::OutputLayer>(mLayer1.outputLayer));
+        mDisplay->injectOutputLayerForTest(
+                std::unique_ptr<compositionengine::OutputLayer>(mLayer2.outputLayer));
+        mDisplay->injectOutputLayerForTest(
+                std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
+    }
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+    Layer mLayer1;
+    Layer mLayer2;
+    LayerNoHWC2Layer mLayer3;
+    StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
+
+    std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
+                                                      DisplayCreationArgsBuilder()
+                                                              .setDisplayId(DEFAULT_DISPLAY_ID)
+                                                              .setPowerAdvisor(&mPowerAdvisor)
+                                                              .build());
+};
+
+/*
  * Basic construction
  */
 
@@ -65,12 +133,10 @@
 
     {
         constexpr DisplayId display2 = DisplayId{546u};
-        auto display = impl::createDisplay(mCompositionEngine,
-                                           DisplayCreationArgsBuilder()
-                                                   .setIsSecure(true)
-                                                   .setDisplayId(display2)
-                                                   .build());
-        EXPECT_TRUE(display->isSecure());
+        auto display =
+                impl::createDisplay(mCompositionEngine,
+                                    DisplayCreationArgsBuilder().setDisplayId(display2).build());
+        EXPECT_FALSE(display->isSecure());
         EXPECT_FALSE(display->isVirtual());
         EXPECT_EQ(display2, display->getId());
     }
@@ -88,73 +154,79 @@
     }
 }
 
-/* ------------------------------------------------------------------------
+/*
  * 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());
+    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());
+    mDisplay->disconnect();
+    EXPECT_FALSE(mDisplay->getId());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::setColorTransform()
  */
 
 TEST_F(DisplayTest, setColorTransformSetsTransform) {
+    // No change does nothing
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = std::nullopt;
+    mDisplay->setColorTransform(refreshArgs);
+
     // Identity matrix sets an identity state value
-    const mat4 identity;
+    const mat4 kIdentity;
 
-    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1);
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
-
-    mDisplay.setColorTransform(identity);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+    refreshArgs.colorTransformMatrix = kIdentity;
+    mDisplay->setColorTransform(refreshArgs);
 
     // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentity = mat4() * 2;
+    const mat4 kNonIdentity = mat4() * 2;
 
-    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+    EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1);
 
-    mDisplay.setColorTransform(nonIdentity);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+    refreshArgs.colorTransformMatrix = kNonIdentity;
+    mDisplay->setColorTransform(refreshArgs);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::setColorMode()
  */
 
 TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
-    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
-    mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+    using ColorProfile = Output::ColorProfile;
 
-    EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+    mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mDisplay->setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile));
+
+    EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _))
+            .WillRepeatedly(Return(ui::Dataspace::UNKNOWN));
 
     // 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);
+    ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
+    ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
+    ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
 
     // Otherwise if the values are unchanged, nothing happens
-    mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
-                          ui::RenderIntent::COLORIMETRIC);
+    mDisplay->setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                                           ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN});
 
-    EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+    EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
 
     // Otherwise if the values are different, updates happen
     EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
@@ -163,47 +235,595 @@
                                    ui::RenderIntent::TONE_MAP_COLORIMETRIC))
             .Times(1);
 
-    mDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                          ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    mDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                           ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+                                           ui::Dataspace::UNKNOWN});
 
-    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
 }
 
 TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
-    impl::Display virtualDisplay{mCompositionEngine,
-                                 DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+    using ColorProfile = Output::ColorProfile;
 
-    virtualDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                                ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    std::shared_ptr<impl::Display> virtualDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgs{true, DEFAULT_DISPLAY_ID})};
 
-    EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+    mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
+    virtualDisplay->setDisplayColorProfileForTest(
+            std::unique_ptr<DisplayColorProfile>(colorProfile));
+
+    EXPECT_CALL(*colorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
+
+    virtualDisplay->setColorProfile(
+            ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                         ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN});
+
+    EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().targetDataspace);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::createDisplayColorProfile()
  */
 
 TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
-    EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr);
-    mDisplay.createDisplayColorProfile(
+    EXPECT_TRUE(mDisplay->getDisplayColorProfile() == nullptr);
+    mDisplay->createDisplayColorProfile(
             DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
                                             DisplayColorProfileCreationArgs::HwcColorModes()});
-    EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr);
+    EXPECT_TRUE(mDisplay->getDisplayColorProfile() != nullptr);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Display::createRenderSurface()
  */
 
 TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
     EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
-    EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr);
-    mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
-    EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr);
+    EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr);
+    mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
+    EXPECT_TRUE(mDisplay->getRenderSurface() != nullptr);
+}
+
+/*
+ * Display::createOutputLayer()
+ */
+
+TEST_F(DisplayTest, createOutputLayerSetsHwcLayer) {
+    sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
+    StrictMock<HWC2::mock::Layer> hwcLayer;
+
+    EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
+
+    auto outputLayer = mDisplay->createOutputLayer(layerFE);
+
+    EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer());
+
+    EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
+    outputLayer.reset();
+}
+
+/*
+ * Display::setReleasedLayers()
+ */
+
+TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNotHwcDisplay) {
+    std::shared_ptr<impl::Display> nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        nonHwcDisplay->setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(layerXLayerFE);
+
+    nonHwcDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = nonHwcDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNoLayersWithQueuedFrames) {
+    sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
+
+    {
+        Output::ReleasedLayers releasedLayers;
+        releasedLayers.emplace_back(layerXLayerFE);
+        mDisplay->setReleasedLayers(std::move(releasedLayers));
+    }
+
+    CompositionRefreshArgs refreshArgs;
+    mDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(1, releasedLayers.size());
+}
+
+TEST_F(DisplayTest, setReleasedLayers) {
+    sp<mock::LayerFE> unknownLayer = new StrictMock<mock::LayerFE>();
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.layersWithQueuedFrames.push_back(mLayer1.layerFE);
+    refreshArgs.layersWithQueuedFrames.push_back(mLayer2.layerFE);
+    refreshArgs.layersWithQueuedFrames.push_back(unknownLayer);
+
+    mDisplay->setReleasedLayers(refreshArgs);
+
+    const auto& releasedLayers = mDisplay->getReleasedLayersForTest();
+    ASSERT_EQ(2, releasedLayers.size());
+    ASSERT_EQ(mLayer1.layerFE.get(), releasedLayers[0].promote().get());
+    ASSERT_EQ(mLayer2.layerFE.get(), releasedLayers[1].promote().get());
+}
+
+/*
+ * Display::chooseCompositionStrategy()
+ */
+
+struct DisplayChooseCompositionStrategyTest : public testing::Test {
+    struct DisplayPartialMock : public impl::Display {
+        DisplayPartialMock(const compositionengine::CompositionEngine& compositionEngine,
+                           const compositionengine::DisplayCreationArgs& args)
+              : impl::Display(args), mCompositionEngine(compositionEngine) {}
+
+        // Sets up the helper functions called by chooseCompositionStrategy to
+        // use a mock implementations.
+        MOCK_CONST_METHOD0(anyLayersRequireClientComposition, bool());
+        MOCK_CONST_METHOD0(allLayersRequireClientComposition, bool());
+        MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
+        MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
+        MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+
+        // compositionengine::Output overrides
+        const OutputCompositionState& getState() const override { return mState; }
+        OutputCompositionState& editState() override { return mState; }
+
+        // compositionengine::impl::Output overrides
+        const CompositionEngine& getCompositionEngine() const override {
+            return mCompositionEngine;
+        };
+
+        // These need implementations though are not expected to be called.
+        MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+        MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+                           compositionengine::OutputLayer*(size_t));
+        MOCK_METHOD2(ensureOutputLayer,
+                     compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+        MOCK_METHOD0(finalizePendingOutputLayers, void());
+        MOCK_METHOD0(clearOutputLayers, void());
+        MOCK_CONST_METHOD1(dumpState, void(std::string&));
+        MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
+        MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+        const compositionengine::CompositionEngine& mCompositionEngine;
+        impl::OutputCompositionState mState;
+    };
+
+    DisplayChooseCompositionStrategyTest() {
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+    }
+
+    StrictMock<android::mock::HWComposer> mHwComposer;
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    StrictMock<DisplayPartialMock>
+            mDisplay{mCompositionEngine,
+                     DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
+};
+
+TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) {
+    std::shared_ptr<impl::Display> nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+    EXPECT_FALSE(nonHwcDisplay->getId());
+
+    nonHwcDisplay->chooseCompositionStrategy();
+
+    auto& state = nonHwcDisplay->getState();
+    EXPECT_TRUE(state.usesClientComposition);
+    EXPECT_FALSE(state.usesDeviceComposition);
+}
+
+TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
+    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
+            .WillOnce(Return(INVALID_OPERATION));
+
+    mDisplay.chooseCompositionStrategy();
+
+    auto& state = mDisplay.getState();
+    EXPECT_TRUE(state.usesClientComposition);
+    EXPECT_FALSE(state.usesDeviceComposition);
+}
+
+TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) {
+    // Since two calls are made to anyLayersRequireClientComposition with different return values,
+    // use a Sequence to control the matching so the values are returned in a known order.
+    Sequence s;
+    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true));
+    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(false));
+
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+
+    mDisplay.chooseCompositionStrategy();
+
+    auto& state = mDisplay.getState();
+    EXPECT_FALSE(state.usesClientComposition);
+    EXPECT_TRUE(state.usesDeviceComposition);
+}
+
+TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
+    android::HWComposer::DeviceRequestedChanges changes{
+            {{nullptr, HWC2::Composition::Client}},
+            HWC2::DisplayRequest::FlipClientTarget,
+            {{nullptr, HWC2::LayerRequest::ClearClientTarget}},
+    };
+
+    // Since two calls are made to anyLayersRequireClientComposition with different return values,
+    // use a Sequence to control the matching so the values are returned in a known order.
+    Sequence s;
+    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true));
+    EXPECT_CALL(mDisplay, anyLayersRequireClientComposition())
+            .InSequence(s)
+            .WillOnce(Return(false));
+
+    EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
+            .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
+    EXPECT_CALL(mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
+    EXPECT_CALL(mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
+    EXPECT_CALL(mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
+    EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+
+    mDisplay.chooseCompositionStrategy();
+
+    auto& state = mDisplay.getState();
+    EXPECT_FALSE(state.usesClientComposition);
+    EXPECT_TRUE(state.usesDeviceComposition);
+}
+
+/*
+ * Display::getSkipColorTransform()
+ */
+
+TEST_F(DisplayTest, getSkipColorTransformDoesNothingIfNonHwcDisplay) {
+    auto nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+    EXPECT_FALSE(nonHwcDisplay->getSkipColorTransform());
+}
+
+TEST_F(DisplayTest, getSkipColorTransformChecksHwcCapability) {
+    EXPECT_CALL(mHwComposer,
+                hasDisplayCapability(std::make_optional(DEFAULT_DISPLAY_ID),
+                                     HWC2::DisplayCapability::SkipClientColorTransform))
+            .WillOnce(Return(true));
+    EXPECT_TRUE(mDisplay->getSkipColorTransform());
+}
+
+/*
+ * Display::anyLayersRequireClientComposition()
+ */
+
+TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsFalse) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+
+    EXPECT_FALSE(mDisplay->anyLayersRequireClientComposition());
+}
+
+TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsTrue) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    EXPECT_TRUE(mDisplay->anyLayersRequireClientComposition());
+}
+
+/*
+ * Display::allLayersRequireClientComposition()
+ */
+
+TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsTrue) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    EXPECT_TRUE(mDisplay->allLayersRequireClientComposition());
+}
+
+TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsFalse) {
+    EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
+    EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
+
+    EXPECT_FALSE(mDisplay->allLayersRequireClientComposition());
+}
+
+/*
+ * Display::applyChangedTypesToLayers()
+ */
+
+TEST_F(DisplayTest, applyChangedTypesToLayersTakesEarlyOutIfNoChangedLayers) {
+    mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes());
+}
+
+TEST_F(DisplayTest, applyChangedTypesToLayersAppliesChanges) {
+    EXPECT_CALL(*mLayer1.outputLayer,
+                applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
+            .Times(1);
+    EXPECT_CALL(*mLayer2.outputLayer,
+                applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE))
+            .Times(1);
+
+    mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{
+            {&mLayer1.hwc2Layer, HWC2::Composition::Client},
+            {&mLayer2.hwc2Layer, HWC2::Composition::Device},
+            {&hwc2LayerUnknown, HWC2::Composition::SolidColor},
+    });
+}
+
+/*
+ * Display::applyDisplayRequests()
+ */
+
+TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesNoRequests) {
+    mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(0));
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.flipClientTarget);
+}
+
+TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesFlipClientTarget) {
+    mDisplay->applyDisplayRequests(HWC2::DisplayRequest::FlipClientTarget);
+
+    auto& state = mDisplay->getState();
+    EXPECT_TRUE(state.flipClientTarget);
+}
+
+TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesWriteClientTargetToOutput) {
+    mDisplay->applyDisplayRequests(HWC2::DisplayRequest::WriteClientTargetToOutput);
+
+    auto& state = mDisplay->getState();
+    EXPECT_FALSE(state.flipClientTarget);
+}
+
+TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesAllRequestFlagsSet) {
+    mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(~0));
+
+    auto& state = mDisplay->getState();
+    EXPECT_TRUE(state.flipClientTarget);
+}
+
+/*
+ * Display::applyLayerRequestsToLayers()
+ */
+
+TEST_F(DisplayTest, applyLayerRequestsToLayersPreparesAllLayers) {
+    EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+
+    mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests());
+}
+
+TEST_F(DisplayTest, applyLayerRequestsToLayers2) {
+    EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+    EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
+
+    EXPECT_CALL(*mLayer1.outputLayer,
+                applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET))
+            .Times(1);
+
+    mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests{
+            {&mLayer1.hwc2Layer, HWC2::LayerRequest::ClearClientTarget},
+            {&hwc2LayerUnknown, HWC2::LayerRequest::ClearClientTarget},
+    });
+}
+
+/*
+ * Display::presentAndGetFrameFences()
+ */
+
+TEST_F(DisplayTest, presentAndGetFrameFencesReturnsNoFencesOnNonHwcDisplay) {
+    auto nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+
+    auto result = nonHwcDisplay->presentAndGetFrameFences();
+
+    ASSERT_TRUE(result.presentFence.get());
+    EXPECT_FALSE(result.presentFence->isValid());
+    EXPECT_EQ(0u, result.layerFences.size());
+}
+
+TEST_F(DisplayTest, presentAndGetFrameFencesReturnsPresentAndLayerFences) {
+    sp<Fence> presentFence = new Fence();
+    sp<Fence> layer1Fence = new Fence();
+    sp<Fence> layer2Fence = new Fence();
+
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+    EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence));
+    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer))
+            .WillOnce(Return(layer1Fence));
+    EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer))
+            .WillOnce(Return(layer2Fence));
+    EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1);
+
+    auto result = mDisplay->presentAndGetFrameFences();
+
+    EXPECT_EQ(presentFence, result.presentFence);
+
+    EXPECT_EQ(2u, result.layerFences.size());
+    ASSERT_EQ(1, result.layerFences.count(&mLayer1.hwc2Layer));
+    EXPECT_EQ(layer1Fence, result.layerFences[&mLayer1.hwc2Layer]);
+    ASSERT_EQ(1, result.layerFences.count(&mLayer2.hwc2Layer));
+    EXPECT_EQ(layer2Fence, result.layerFences[&mLayer2.hwc2Layer]);
+}
+
+/*
+ * Display::setExpensiveRenderingExpected()
+ */
+
+TEST_F(DisplayTest, setExpensiveRenderingExpectedForwardsToPowerAdvisor) {
+    EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, true)).Times(1);
+    mDisplay->setExpensiveRenderingExpected(true);
+
+    EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false)).Times(1);
+    mDisplay->setExpensiveRenderingExpected(false);
+}
+
+/*
+ * Display::finishFrame()
+ */
+
+TEST_F(DisplayTest, finishFrameDoesNotSkipCompositionIfNotDirtyOnHwcDisplay) {
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect no calls to queueBuffer if composition was skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+
+    // Expect a call to signal no expensive rendering since there is no client composition.
+    EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false));
+
+    mDisplay->editState().isEnabled = true;
+    mDisplay->editState().usesClientComposition = false;
+    mDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    mDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = false;
+
+    mDisplay->finishFrame(refreshArgs);
+}
+
+TEST_F(DisplayTest, finishFrameSkipsCompositionIfNotDirty) {
+    std::shared_ptr<impl::Display> nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect no calls to queueBuffer if composition was skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0);
+
+    nonHwcDisplay->editState().isEnabled = true;
+    nonHwcDisplay->editState().usesClientComposition = false;
+    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = false;
+
+    nonHwcDisplay->finishFrame(refreshArgs);
+}
+
+TEST_F(DisplayTest, finishFramePerformsCompositionIfDirty) {
+    std::shared_ptr<impl::Display> nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect a single call to queueBuffer when composition is not skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+
+    nonHwcDisplay->editState().isEnabled = true;
+    nonHwcDisplay->editState().usesClientComposition = false;
+    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1));
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = false;
+
+    nonHwcDisplay->finishFrame(refreshArgs);
+}
+
+TEST_F(DisplayTest, finishFramePerformsCompositionIfRepaintEverything) {
+    std::shared_ptr<impl::Display> nonHwcDisplay{
+            impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+
+    mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
+    nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
+
+    // We expect a single call to queueBuffer when composition is not skipped.
+    EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1);
+
+    nonHwcDisplay->editState().isEnabled = true;
+    nonHwcDisplay->editState().usesClientComposition = false;
+    nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1);
+    nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION;
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.repaintEverything = true;
+
+    nonHwcDisplay->finishFrame(refreshArgs);
+}
+
+/*
+ * Display functional tests
+ */
+
+struct DisplayFunctionalTest : public testing::Test {
+    class Display : public impl::Display {
+    public:
+        explicit Display(const compositionengine::DisplayCreationArgs& args)
+              : impl::Display(args) {}
+
+        using impl::Display::injectOutputLayerForTest;
+        virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+    };
+
+    static std::shared_ptr<Display> createDisplay(
+            const compositionengine::CompositionEngine& compositionEngine,
+            compositionengine::DisplayCreationArgs&& args) {
+        return impl::createDisplayTemplated<Display>(compositionEngine, args);
+    }
+
+    DisplayFunctionalTest() {
+        EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+        mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    NiceMock<android::mock::HWComposer> mHwComposer;
+    NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+    NiceMock<mock::CompositionEngine> mCompositionEngine;
+    sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
+    sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
+    std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
+                                                      DisplayCreationArgsBuilder()
+                                                              .setDisplayId(DEFAULT_DISPLAY_ID)
+                                                              .setPowerAdvisor(&mPowerAdvisor)
+                                                              .build());
+    impl::RenderSurface* mRenderSurface =
+            new impl::RenderSurface{mCompositionEngine, *mDisplay,
+                                    RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+                                                              DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+                                                              mDisplaySurface}};
+};
+
+TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
+    InSequence seq;
+
+    mDisplay->editState().isEnabled = true;
+
+    EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+    EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
+
+    mDisplay->postFramebuffer();
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
deleted file mode 100644
index 26115a3..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 <gtest/gtest.h>
-
-#include <compositionengine/LayerCreationArgs.h>
-#include <compositionengine/impl/Layer.h>
-#include <compositionengine/mock/CompositionEngine.h>
-#include <compositionengine/mock/LayerFE.h>
-
-namespace android::compositionengine {
-namespace {
-
-using testing::StrictMock;
-
-class LayerTest : public testing::Test {
-public:
-    ~LayerTest() override = default;
-
-    StrictMock<mock::CompositionEngine> mCompositionEngine;
-    sp<LayerFE> mLayerFE = new StrictMock<mock::LayerFE>();
-    impl::Layer mLayer{mCompositionEngine, LayerCreationArgs{mLayerFE}};
-};
-
-/* ------------------------------------------------------------------------
- * Basic construction
- */
-
-TEST_F(LayerTest, canInstantiateLayer) {}
-
-} // namespace
-} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 7fd6541..be89c1a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -20,13 +20,21 @@
 #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>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <ui/GraphicTypes.h>
 #include "DisplayHardware/HWC2.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace HWC2 {
 namespace mock {
 
@@ -57,6 +65,8 @@
     MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t));
 
     MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
+    MOCK_METHOD3(setLayerGenericMetadata,
+                 Error(const std::string&, bool, const std::vector<uint8_t>&));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 94349de..02226ab 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -19,8 +19,15 @@
 #include <compositionengine/Output.h>
 #include <gmock/gmock.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "DisplayHardware/HWComposer.h"
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android {
 namespace mock {
 
@@ -29,7 +36,7 @@
     HWComposer();
     ~HWComposer() override;
 
-    MOCK_METHOD2(registerCallback, void(HWC2::ComposerCallback*, int32_t));
+    MOCK_METHOD2(setConfiguration, void(HWC2::ComposerCallback*, int32_t));
     MOCK_CONST_METHOD3(getDisplayIdentificationData,
                        bool(hwc2_display_t, uint8_t*, DisplayIdentificationData*));
     MOCK_CONST_METHOD1(hasCapability, bool(HWC2::Capability));
@@ -40,7 +47,9 @@
                  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, const compositionengine::Output&));
+    MOCK_METHOD3(getDeviceCompositionChanges,
+                 status_t(DisplayId, bool,
+                          std::optional<android::HWComposer::DeviceRequestedChanges>*));
     MOCK_METHOD5(setClientTarget,
                  status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
                           ui::Dataspace));
@@ -50,8 +59,6 @@
     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>&));
@@ -81,6 +88,17 @@
     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(getDisplayConnectionType, DisplayConnectionType(DisplayId));
+    MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
+    MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
+    MOCK_METHOD4(setActiveConfigWithConstraints,
+                 status_t(DisplayId, size_t, const HWC2::VsyncPeriodChangeConstraints&,
+                          HWC2::VsyncPeriodChangeTimeline*));
+    MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
+    MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<HWC2::ContentType>*));
+    MOCK_METHOD2(setContentType, status_t(DisplayId, HWC2::ContentType));
+    MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata,
+                       const std::unordered_map<std::string, bool>&());
 
     MOCK_CONST_METHOD1(dump, void(std::string&));
     MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
similarity index 67%
rename from services/surfaceflinger/CompositionEngine/mock/Layer.cpp
rename to services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
index 08483cb..85b9403 100644
--- a/services/surfaceflinger/CompositionEngine/mock/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp
@@ -1,6 +1,6 @@
 /*
  * 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,13 +14,21 @@
  * limitations under the License.
  */
 
-#include <compositionengine/mock/Layer.h>
+#include "MockPowerAdvisor.h"
 
-namespace android::compositionengine::mock {
+namespace android {
+namespace Hwc2 {
+
+// This will go away once PowerAdvisor is moved into the "backend" library
+PowerAdvisor::~PowerAdvisor() = default;
+
+namespace mock {
 
 // The Google Mock documentation recommends explicit non-header instantiations
 // for better compile time performance.
-Layer::Layer() = default;
-Layer::~Layer() = default;
+PowerAdvisor::PowerAdvisor() = default;
+PowerAdvisor::~PowerAdvisor() = default;
 
-} // namespace android::compositionengine::mock
+} // namespace mock
+} // namespace Hwc2
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
similarity index 62%
copy from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
copy to services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index a7fd667..c5a73f2 100644
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -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,24 @@
  * limitations under the License.
  */
 
-#include "mock/gui/MockGraphicBufferProducer.h"
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "DisplayHardware/PowerAdvisor.h"
 
 namespace android {
+namespace Hwc2 {
 namespace mock {
 
-// Explicit default instantiation is recommended.
-GraphicBufferProducer::GraphicBufferProducer() = default;
-GraphicBufferProducer::~GraphicBufferProducer() = default;
+class PowerAdvisor : public android::Hwc2::PowerAdvisor {
+public:
+    PowerAdvisor();
+    ~PowerAdvisor() override;
+
+    MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
+};
 
 } // namespace mock
-} // namespace android
\ No newline at end of file
+} // namespace Hwc2
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 2060c5a..1b5617c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -15,26 +15,26 @@
  */
 
 #include <compositionengine/impl/OutputLayer.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/CompositionEngine.h>
-#include <compositionengine/mock/Layer.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/Output.h>
 #include <gtest/gtest.h>
 
 #include "MockHWC2.h"
 #include "MockHWComposer.h"
-#include "RectMatcher.h"
+#include "RegionMatcher.h"
 
 namespace android::compositionengine {
 namespace {
 
 using testing::_;
+using testing::InSequence;
 using testing::Return;
 using testing::ReturnRef;
 using testing::StrictMock;
 
-constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
-
 constexpr auto TR_IDENT = 0u;
 constexpr auto TR_FLP_H = HAL_TRANSFORM_FLIP_H;
 constexpr auto TR_FLP_V = HAL_TRANSFORM_FLIP_V;
@@ -44,26 +44,49 @@
 
 const std::string kOutputName{"Test Output"};
 
-class OutputLayerTest : public testing::Test {
-public:
+MATCHER_P(ColorEq, expected, "") {
+    *result_listener << "Colors are not equal\n";
+    *result_listener << "expected " << expected.r << " " << expected.g << " " << expected.b << " "
+                     << expected.a << "\n";
+    *result_listener << "actual " << arg.r << " " << arg.g << " " << arg.b << " " << arg.a << "\n";
+
+    return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a;
+}
+
+struct OutputLayerTest : public testing::Test {
+    struct OutputLayer final : public impl::OutputLayer {
+        OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE)
+              : mOutput(output), mLayerFE(layerFE) {}
+        ~OutputLayer() override = default;
+
+        // compositionengine::OutputLayer overrides
+        const compositionengine::Output& getOutput() const override { return mOutput; }
+        compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+        const impl::OutputLayerCompositionState& getState() const override { return mState; }
+        impl::OutputLayerCompositionState& editState() override { return mState; }
+
+        // compositionengine::impl::OutputLayer overrides
+        void dumpState(std::string& out) const override { mState.dump(out); }
+
+        const compositionengine::Output& mOutput;
+        sp<compositionengine::LayerFE> mLayerFE;
+        impl::OutputLayerCompositionState mState;
+    };
+
     OutputLayerTest() {
         EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE"));
         EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName));
 
-        EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState));
+        EXPECT_CALL(*mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
         EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
     }
 
-    ~OutputLayerTest() override = default;
-
     compositionengine::mock::Output mOutput;
-    std::shared_ptr<compositionengine::mock::Layer> mLayer{
-            new StrictMock<compositionengine::mock::Layer>()};
     sp<compositionengine::mock::LayerFE> mLayerFE{
             new StrictMock<compositionengine::mock::LayerFE>()};
-    impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE};
+    OutputLayer mOutputLayer{mOutput, mLayerFE};
 
-    impl::LayerCompositionState mLayerState;
+    LayerFECompositionState mLayerFEState;
     impl::OutputCompositionState mOutputState;
 };
 
@@ -74,35 +97,134 @@
 TEST_F(OutputLayerTest, canInstantiateOutputLayer) {}
 
 /*
- * OutputLayer::initialize()
+ * OutputLayer::setHwcLayer()
  */
 
-TEST_F(OutputLayerTest, initializingOutputLayerWithoutHwcDoesNothingInteresting) {
+TEST_F(OutputLayerTest, settingNullHwcLayerSetsEmptyHwcState) {
     StrictMock<compositionengine::mock::CompositionEngine> compositionEngine;
 
-    mOutputLayer.initialize(compositionEngine, std::nullopt);
+    mOutputLayer.setHwcLayer(nullptr);
 
     EXPECT_FALSE(mOutputLayer.getState().hwc);
 }
 
-TEST_F(OutputLayerTest, initializingOutputLayerWithHwcDisplayCreatesHwcLayer) {
-    StrictMock<compositionengine::mock::CompositionEngine> compositionEngine;
-    StrictMock<android::mock::HWComposer> hwc;
-    StrictMock<HWC2::mock::Layer> hwcLayer;
+TEST_F(OutputLayerTest, settingHwcLayerSetsHwcState) {
+    auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>();
 
-    EXPECT_CALL(compositionEngine, getHwComposer()).WillOnce(ReturnRef(hwc));
-    EXPECT_CALL(hwc, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer));
-
-    mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID);
+    mOutputLayer.setHwcLayer(hwcLayer);
 
     const auto& outputLayerState = mOutputLayer.getState();
     ASSERT_TRUE(outputLayerState.hwc);
 
     const auto& hwcState = *outputLayerState.hwc;
-    EXPECT_EQ(&hwcLayer, hwcState.hwcLayer.get());
+    EXPECT_EQ(hwcLayer, hwcState.hwcLayer);
+}
 
-    EXPECT_CALL(hwc, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer));
-    mOutputLayer.editState().hwc.reset();
+/*
+ * OutputLayer::calculateOutputSourceCrop()
+ */
+
+struct OutputLayerSourceCropTest : public OutputLayerTest {
+    OutputLayerSourceCropTest() {
+        // Set reasonable default values for a simple case. Each test will
+        // set one specific value to something different.
+        mLayerFEState.geomUsesSourceCrop = true;
+        mLayerFEState.geomContentCrop = Rect{0, 0, 1920, 1080};
+        mLayerFEState.transparentRegionHint = Region{};
+        mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+        mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+        mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomBufferTransform = TR_IDENT;
+
+        mOutputState.viewport = Rect{0, 0, 1920, 1080};
+    }
+
+    FloatRect calculateOutputSourceCrop() {
+        mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse();
+
+        return mOutputLayer.calculateOutputSourceCrop();
+    }
+};
+
+TEST_F(OutputLayerSourceCropTest, computesEmptyIfSourceCropNotUsed) {
+    mLayerFEState.geomUsesSourceCrop = false;
+
+    const FloatRect expected{};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) {
+    const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) {
+    mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+
+    const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) {
+    mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
+    mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+
+    const FloatRect expected{0.f, 0.f, 1080.f, 1080.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) {
+    struct Entry {
+        uint32_t bufferInvDisplay;
+        uint32_t buffer;
+        uint32_t display;
+        FloatRect expected;
+    };
+    // Not an exhaustive list of cases, but hopefully enough.
+    const std::array<Entry, 12> testData = {
+            // clang-format off
+            //             inv      buffer      display     expected
+            /*  0 */ Entry{false,   TR_IDENT,   TR_IDENT,   FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  1 */ Entry{false,   TR_IDENT,   TR_ROT_90,  FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  2 */ Entry{false,   TR_IDENT,   TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  3 */ Entry{false,   TR_IDENT,   TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+            /*  4 */ Entry{true,    TR_IDENT,   TR_IDENT,   FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  5 */ Entry{true,    TR_IDENT,   TR_ROT_90,  FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  6 */ Entry{true,    TR_IDENT,   TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  7 */ Entry{true,    TR_IDENT,   TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+            /*  8 */ Entry{false,   TR_IDENT,   TR_IDENT,   FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /*  9 */ Entry{false,   TR_ROT_90,  TR_ROT_90,  FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /* 10 */ Entry{false,   TR_ROT_180, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+            /* 11 */ Entry{false,   TR_ROT_270, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}},
+
+            // clang-format on
+    };
+
+    for (size_t i = 0; i < testData.size(); i++) {
+        const auto& entry = testData[i];
+
+        mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay;
+        mLayerFEState.geomBufferTransform = entry.buffer;
+        mOutputState.orientation = entry.display;
+
+        EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
+    }
+}
+
+TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) {
+    mLayerFEState.geomContentCrop = Rect{0, 0, 960, 540};
+
+    const FloatRect expected{0.f, 0.f, 960.f, 540.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
+}
+
+TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
+    mOutputState.viewport = Rect{0, 0, 960, 540};
+
+    const FloatRect expected{0.f, 0.f, 960.f, 540.f};
+    EXPECT_THAT(calculateOutputSourceCrop(), expected);
 }
 
 /*
@@ -114,20 +236,19 @@
         // Set reasonable default values for a simple case. Each test will
         // set one specific value to something different.
 
-        mLayerState.frontEnd.geomActiveTransparentRegion = Region{};
-        mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT};
-        mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
-        mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080};
-        mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
+        mLayerFEState.transparentRegionHint = Region{};
+        mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+        mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
+        mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
+        mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
 
         mOutputState.viewport = Rect{0, 0, 1920, 1080};
         mOutputState.transform = ui::Transform{TR_IDENT};
     }
 
     Rect calculateOutputDisplayFrame() {
-        mLayerState.frontEnd.geomInverseLayerTransform =
-                mLayerState.frontEnd.geomLayerTransform.inverse();
+        mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse();
 
         return mOutputLayer.calculateOutputDisplayFrame();
     }
@@ -135,50 +256,50 @@
 
 TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) {
     const Rect expected{0, 0, 1920, 1080};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
-    mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}};
+    mLayerFEState.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}};
     const Rect expected{0, 0, 0, 0};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
-    mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
     const Rect expected{100, 200, 300, 500};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
-    mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500};
-    mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
+    mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+    mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
     const Rect expected{1420, 100, 1720, 300};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
-    mLayerState.frontEnd.geomCrop = Rect{};
+    mLayerFEState.geomCrop = Rect{};
     const Rect expected{0, 0, 1920, 1080};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
-TEST_F(OutputLayerDisplayFrameTest, geomLayerSnapToBoundsAffectsFrame) {
-    mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
+TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) {
+    mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
     const Rect expected{0, 0, 960, 540};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
     mOutputState.viewport = Rect{0, 0, 960, 540};
     const Rect expected{0, 0, 960, 540};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) {
     mOutputState.transform = ui::Transform{HAL_TRANSFORM_ROT_90};
     const Rect expected{-1080, 0, 0, 1920};
-    EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+    EXPECT_THAT(calculateOutputDisplayFrame(), expected);
 }
 
 /*
@@ -186,7 +307,7 @@
  */
 
 TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) {
-    mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false;
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
 
     struct Entry {
         uint32_t layer;
@@ -233,15 +354,260 @@
     for (size_t i = 0; i < testData.size(); i++) {
         const auto& entry = testData[i];
 
-        mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080);
-        mLayerState.frontEnd.geomBufferTransform = entry.buffer;
+        mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
+        mLayerFEState.geomBufferTransform = entry.buffer;
         mOutputState.orientation = entry.display;
+        mOutputState.transform = ui::Transform{entry.display};
 
         auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
         EXPECT_EQ(entry.expected, actual) << "entry " << i;
     }
 }
 
+TEST_F(OutputLayerTest,
+       calculateOutputRelativeBufferTransformTestWithOfBufferUsesDisplayInverseTransform) {
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = true;
+
+    struct Entry {
+        uint32_t layer;
+        uint32_t buffer;
+        uint32_t display;
+        uint32_t expected;
+    };
+    // Not an exhaustive list of cases, but hopefully enough.
+    const std::array<Entry, 24> testData = {
+            // clang-format off
+            //             layer       buffer      display     expected
+            /*  0 */ Entry{TR_IDENT,   TR_IDENT,   TR_IDENT,   TR_IDENT},
+            /*  1 */ Entry{TR_IDENT,   TR_IDENT,   TR_ROT_90,  TR_IDENT},
+            /*  2 */ Entry{TR_IDENT,   TR_IDENT,   TR_ROT_180, TR_IDENT},
+            /*  3 */ Entry{TR_IDENT,   TR_IDENT,   TR_ROT_270, TR_IDENT},
+
+            /*  4 */ Entry{TR_IDENT,   TR_FLP_H,   TR_IDENT,   TR_FLP_H},
+            /*  5 */ Entry{TR_IDENT,   TR_FLP_H,   TR_ROT_90,  TR_FLP_H},
+            /*  6 */ Entry{TR_IDENT,   TR_FLP_H,   TR_ROT_180, TR_FLP_H},
+            /*  7 */ Entry{TR_IDENT,   TR_FLP_H,   TR_ROT_270, TR_FLP_H},
+
+            /*  8 */ Entry{TR_IDENT,   TR_FLP_V,   TR_IDENT,   TR_FLP_V},
+            /*  9 */ Entry{TR_IDENT,   TR_ROT_90,  TR_ROT_90,  TR_ROT_90},
+            /* 10 */ Entry{TR_IDENT,   TR_ROT_180, TR_ROT_180, TR_ROT_180},
+            /* 11 */ Entry{TR_IDENT,   TR_ROT_270, TR_ROT_270, TR_ROT_270},
+
+            /* 12 */ Entry{TR_ROT_90,  TR_IDENT,   TR_IDENT,   TR_IDENT},
+            /* 13 */ Entry{TR_ROT_90,  TR_FLP_H,   TR_ROT_90,  TR_FLP_H},
+            /* 14 */ Entry{TR_ROT_90,  TR_IDENT,   TR_ROT_180, TR_IDENT},
+            /* 15 */ Entry{TR_ROT_90,  TR_FLP_H,   TR_ROT_270, TR_FLP_H},
+
+            /* 16 */ Entry{TR_ROT_180, TR_FLP_H,   TR_IDENT,   TR_FLP_H},
+            /* 17 */ Entry{TR_ROT_180, TR_IDENT,   TR_ROT_90,  TR_IDENT},
+            /* 18 */ Entry{TR_ROT_180, TR_FLP_H,   TR_ROT_180, TR_FLP_H},
+            /* 19 */ Entry{TR_ROT_180, TR_IDENT,   TR_ROT_270, TR_IDENT},
+
+            /* 20 */ Entry{TR_ROT_270, TR_IDENT,   TR_IDENT,   TR_IDENT},
+            /* 21 */ Entry{TR_ROT_270, TR_FLP_H,   TR_ROT_90,  TR_FLP_H},
+            /* 22 */ Entry{TR_ROT_270, TR_FLP_H,   TR_ROT_180, TR_FLP_H},
+            /* 23 */ Entry{TR_ROT_270, TR_IDENT,   TR_ROT_270, TR_IDENT},
+            // clang-format on
+    };
+
+    for (size_t i = 0; i < testData.size(); i++) {
+        const auto& entry = testData[i];
+
+        mLayerFEState.geomLayerTransform = ui::Transform{entry.layer};
+        mLayerFEState.geomBufferTransform = entry.buffer;
+        mOutputState.orientation = entry.display;
+        mOutputState.transform = ui::Transform{entry.display};
+
+        auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
+        EXPECT_EQ(entry.expected, actual) << "entry " << i;
+    }
+}
+
+/*
+ * OutputLayer::updateCompositionState()
+ */
+
+struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer {
+    OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output,
+                                                    sp<compositionengine::LayerFE> layerFE)
+          : mOutput(output), mLayerFE(layerFE) {}
+    // Mock everything called by updateCompositionState to simplify testing it.
+    MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect());
+    MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect());
+    MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t());
+
+    // compositionengine::OutputLayer overrides
+    const compositionengine::Output& getOutput() const override { return mOutput; }
+    compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; }
+    const impl::OutputLayerCompositionState& getState() const override { return mState; }
+    impl::OutputLayerCompositionState& editState() override { return mState; }
+
+    // These need implementations though are not expected to be called.
+    MOCK_CONST_METHOD1(dumpState, void(std::string&));
+
+    const compositionengine::Output& mOutput;
+    sp<compositionengine::LayerFE> mLayerFE;
+    impl::OutputLayerCompositionState mState;
+};
+
+struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest {
+public:
+    OutputLayerUpdateCompositionStateTest() {
+        EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState));
+        EXPECT_CALL(mOutput, getDisplayColorProfile())
+                .WillRepeatedly(Return(&mDisplayColorProfile));
+        EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(true));
+    }
+
+    ~OutputLayerUpdateCompositionStateTest() = default;
+
+    void setupGeometryChildCallValues() {
+        EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop));
+        EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame));
+        EXPECT_CALL(mOutputLayer, calculateOutputRelativeBufferTransform())
+                .WillOnce(Return(mBufferTransform));
+    }
+
+    void validateComputedGeometryState() {
+        const auto& state = mOutputLayer.getState();
+        EXPECT_EQ(kSourceCrop, state.sourceCrop);
+        EXPECT_EQ(kDisplayFrame, state.displayFrame);
+        EXPECT_EQ(static_cast<Hwc2::Transform>(mBufferTransform), state.bufferTransform);
+    }
+
+    const FloatRect kSourceCrop{1.f, 2.f, 3.f, 4.f};
+    const Rect kDisplayFrame{11, 12, 13, 14};
+    uint32_t mBufferTransform{21};
+
+    using OutputLayer = OutputLayerPartialMockForUpdateCompositionState;
+    StrictMock<OutputLayer> mOutputLayer{mOutput, mLayerFE};
+    StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
+};
+
+TEST_F(OutputLayerUpdateCompositionStateTest, doesNothingIfNoFECompositionState) {
+    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    mOutputLayer.updateCompositionState(true, false);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) {
+    mLayerFEState.isSecure = true;
+    mOutputState.isSecure = true;
+    mOutputLayer.editState().forceClientComposition = true;
+
+    setupGeometryChildCallValues();
+
+    mOutputLayer.updateCompositionState(true, false);
+
+    validateComputedGeometryState();
+
+    EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       alsoSetsForceCompositionIfSecureLayerOnNonsecureOutput) {
+    mLayerFEState.isSecure = true;
+    mOutputState.isSecure = false;
+
+    setupGeometryChildCallValues();
+
+    mOutputLayer.updateCompositionState(true, false);
+
+    validateComputedGeometryState();
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       alsoSetsForceCompositionIfUnsupportedBufferTransform) {
+    mLayerFEState.isSecure = true;
+    mOutputState.isSecure = true;
+
+    mBufferTransform = ui::Transform::ROT_INVALID;
+
+    setupGeometryChildCallValues();
+
+    mOutputLayer.updateCompositionState(true, false);
+
+    validateComputedGeometryState();
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) {
+    mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
+    mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB;
+
+    // If the layer is not colorspace agnostic, the output layer dataspace
+    // should use the layers requested colorspace.
+    mLayerFEState.isColorspaceAgnostic = false;
+
+    mOutputLayer.updateCompositionState(false, false);
+
+    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace);
+
+    // If the layer is colorspace agnostic, the output layer dataspace
+    // should use the colorspace chosen for the whole output.
+    mLayerFEState.isColorspaceAgnostic = true;
+
+    mOutputLayer.updateCompositionState(false, false);
+
+    EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
+    mOutputLayer.editState().forceClientComposition = false;
+
+    mOutputLayer.updateCompositionState(false, false);
+
+    EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       doesNotClearForceClientCompositionIfNotDoingGeometry) {
+    mOutputLayer.editState().forceClientComposition = true;
+
+    mOutputLayer.updateCompositionState(false, false);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromFrontEndFlagAtAnyTime) {
+    mLayerFEState.forceClientComposition = true;
+    mOutputLayer.editState().forceClientComposition = false;
+
+    mOutputLayer.updateCompositionState(false, false);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest,
+       clientCompositionForcedFromUnsupportedDataspaceAtAnyTime) {
+    mOutputLayer.editState().forceClientComposition = false;
+    EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false));
+
+    mOutputLayer.updateCompositionState(false, false);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
+TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromArgumentFlag) {
+    mLayerFEState.forceClientComposition = false;
+    mOutputLayer.editState().forceClientComposition = false;
+
+    mOutputLayer.updateCompositionState(false, true);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+
+    mOutputLayer.editState().forceClientComposition = false;
+
+    setupGeometryChildCallValues();
+
+    mOutputLayer.updateCompositionState(true, true);
+
+    EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition);
+}
+
 /*
  * OutputLayer::writeStateToHWC()
  */
@@ -256,8 +622,25 @@
     static constexpr float kAlpha = 51.f;
     static constexpr uint32_t kType = 61u;
     static constexpr uint32_t kAppId = 62u;
+    static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71);
+    static constexpr int kSupportedPerFrameMetadata = 101;
+    static constexpr int kExpectedHwcSlot = 0;
+    static constexpr bool kLayerGenericMetadata1Mandatory = true;
+    static constexpr bool kLayerGenericMetadata2Mandatory = true;
 
+    static const half4 kColor;
     static const Rect kDisplayFrame;
+    static const Region kOutputSpaceVisibleRegion;
+    static const mat4 kColorTransform;
+    static const Region kSurfaceDamage;
+    static const HdrMetadata kHdrMetadata;
+    static native_handle_t* kSidebandStreamHandle;
+    static const sp<GraphicBuffer> kBuffer;
+    static const sp<Fence> kFence;
+    static const std::string kLayerGenericMetadata1Key;
+    static const std::vector<uint8_t> kLayerGenericMetadata1Value;
+    static const std::string kLayerGenericMetadata2Key;
+    static const std::vector<uint8_t> kLayerGenericMetadata2Value;
 
     OutputLayerWriteStateToHWCTest() {
         auto& outputLayerState = mOutputLayer.editState();
@@ -267,11 +650,36 @@
         outputLayerState.sourceCrop = kSourceCrop;
         outputLayerState.z = kZOrder;
         outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
+        outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
+        outputLayerState.dataspace = kDataspace;
 
-        mLayerState.frontEnd.blendMode = kBlendMode;
-        mLayerState.frontEnd.alpha = kAlpha;
-        mLayerState.frontEnd.type = kType;
-        mLayerState.frontEnd.appId = kAppId;
+        mLayerFEState.blendMode = kBlendMode;
+        mLayerFEState.alpha = kAlpha;
+        mLayerFEState.type = kType;
+        mLayerFEState.appId = kAppId;
+        mLayerFEState.colorTransform = kColorTransform;
+        mLayerFEState.color = kColor;
+        mLayerFEState.surfaceDamage = kSurfaceDamage;
+        mLayerFEState.hdrMetadata = kHdrMetadata;
+        mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false);
+        mLayerFEState.buffer = kBuffer;
+        mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
+        mLayerFEState.acquireFence = kFence;
+
+        EXPECT_CALL(mOutput, getDisplayColorProfile())
+                .WillRepeatedly(Return(&mDisplayColorProfile));
+        EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
+                .WillRepeatedly(Return(kSupportedPerFrameMetadata));
+    }
+
+    // Some tests may need to simulate unsupported HWC calls
+    enum class SimulateUnsupported { None, ColorTransform };
+
+    void includeGenericLayerMetadataInState() {
+        mLayerFEState.metadata[kLayerGenericMetadata1Key] = {kLayerGenericMetadata1Mandatory,
+                                                             kLayerGenericMetadata1Value};
+        mLayerFEState.metadata[kLayerGenericMetadata2Key] = {kLayerGenericMetadata2Mandatory,
+                                                             kLayerGenericMetadata2Value};
     }
 
     void expectGeometryCommonCalls() {
@@ -287,10 +695,88 @@
         EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError));
     }
 
+    void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) {
+        EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion)))
+                .WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError));
+        EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
+                .WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
+                                         ? HWC2::Error::Unsupported
+                                         : HWC2::Error::None));
+        EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage)))
+                .WillOnce(Return(kError));
+    }
+
+    void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
+        EXPECT_CALL(*mHwcLayer, setCompositionType(static_cast<HWC2::Composition>(compositionType)))
+                .WillOnce(Return(kError));
+    }
+
+    void expectNoSetCompositionTypeCall() {
+        EXPECT_CALL(*mHwcLayer, setCompositionType(_)).Times(0);
+    }
+
+    void expectSetColorCall() {
+        hwc_color_t color = {static_cast<uint8_t>(std::round(kColor.r * 255)),
+                             static_cast<uint8_t>(std::round(kColor.g * 255)),
+                             static_cast<uint8_t>(std::round(kColor.b * 255)), 255};
+
+        EXPECT_CALL(*mHwcLayer, setColor(ColorEq(color))).WillOnce(Return(kError));
+    }
+
+    void expectSetSidebandHandleCall() {
+        EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle));
+    }
+
+    void expectSetHdrMetadataAndBufferCalls() {
+        EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata));
+        EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence));
+    }
+
+    void expectGenericLayerMetadataCalls() {
+        // Note: Can be in any order.
+        EXPECT_CALL(*mHwcLayer,
+                    setLayerGenericMetadata(kLayerGenericMetadata1Key,
+                                            kLayerGenericMetadata1Mandatory,
+                                            kLayerGenericMetadata1Value));
+        EXPECT_CALL(*mHwcLayer,
+                    setLayerGenericMetadata(kLayerGenericMetadata2Key,
+                                            kLayerGenericMetadata2Mandatory,
+                                            kLayerGenericMetadata2Value));
+    }
+
     std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+    StrictMock<mock::DisplayColorProfile> mDisplayColorProfile;
 };
 
+const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f,
+                                                   84.f / 255.f};
 const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044};
+const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{
+        Rect{1005, 1006, 1007, 1008}};
+const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{
+        1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
+        1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024,
+};
+const Region OutputLayerWriteStateToHWCTest::kSurfaceDamage{Rect{1025, 1026, 1027, 1028}};
+const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029};
+native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle =
+        reinterpret_cast<native_handle_t*>(1031);
+const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer;
+const sp<Fence> OutputLayerWriteStateToHWCTest::kFence;
+const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Key =
+        "com.example.metadata.1";
+const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata1Value{{1, 2, 3}};
+const std::string OutputLayerWriteStateToHWCTest::kLayerGenericMetadata2Key =
+        "com.example.metadata.2";
+const std::vector<uint8_t> OutputLayerWriteStateToHWCTest::kLayerGenericMetadata2Value{
+        {4, 5, 6, 7}};
+
+TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoFECompositionState) {
+    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    mOutputLayer.writeStateToHWC(true);
+}
 
 TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) {
     mOutputLayer.editState().hwc.reset();
@@ -304,11 +790,330 @@
     mOutputLayer.writeStateToHWC(true);
 }
 
-TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) {
+TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) {
     expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+
+    expectNoSetCompositionTypeCall();
 
     mOutputLayer.writeStateToHWC(true);
 }
 
+TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
+    mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
+    mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+    // This test simulates a scenario where displayInstallOrientation is set to
+    // ROT_90. This only has an effect on the transform; orientation stays 0 (see
+    // DisplayDevice::setProjection).
+    mOutputState.orientation = TR_IDENT;
+    mOutputState.transform = ui::Transform{TR_ROT_90};
+    // Buffers are pre-rotated based on the transform hint (ROT_90); their
+    // geomBufferTransform is set to the inverse transform.
+    mLayerFEState.geomBufferTransform = TR_ROT_270;
+
+    EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform());
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+
+    // Setting the composition type should happen before setting the color. We
+    // check this in this test only by setting up an testing::InSeqeuence
+    // instance before setting up the two expectations.
+    InSequence s;
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
+    expectSetColorCall();
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+
+    expectPerFrameCommonCalls();
+    expectSetSidebandHandleCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
+    (*mOutputLayer.editState().hwc).hwcCompositionType =
+            Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+    expectSetColorCall();
+    expectNoSetCompositionTypeCall();
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform);
+    expectSetColorCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
+    mOutputLayer.editState().forceClientComposition = true;
+
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+
+    expectPerFrameCommonCalls();
+    expectSetColorCall();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeGenericLayerMetadataInState();
+
+    expectGeometryCommonCalls();
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectGenericLayerMetadataCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(true);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
+    mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+    includeGenericLayerMetadataInState();
+
+    expectPerFrameCommonCalls();
+    expectSetHdrMetadataAndBufferCalls();
+    expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+
+    mOutputLayer.writeStateToHWC(false);
+}
+
+/*
+ * OutputLayer::writeCursorPositionToHWC()
+ */
+
+struct OutputLayerWriteCursorPositionToHWCTest : public OutputLayerTest {
+    static constexpr int kDefaultTransform = TR_IDENT;
+    static constexpr HWC2::Error kDefaultError = HWC2::Error::Unsupported;
+
+    static const Rect kDefaultDisplayViewport;
+    static const Rect kDefaultCursorFrame;
+
+    OutputLayerWriteCursorPositionToHWCTest() {
+        auto& outputLayerState = mOutputLayer.editState();
+        outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer);
+
+        mLayerFEState.cursorFrame = kDefaultCursorFrame;
+
+        mOutputState.viewport = kDefaultDisplayViewport;
+        mOutputState.transform = ui::Transform{kDefaultTransform};
+    }
+
+    std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()};
+};
+
+const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultDisplayViewport{0, 0, 1920, 1080};
+const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultCursorFrame{1, 2, 3, 4};
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, doesNothingIfNoFECompositionState) {
+    EXPECT_CALL(*mLayerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCHandlesNoHwcState) {
+    mOutputLayer.editState().hwc.reset();
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCWritesStateToHWC) {
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(1, 2)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCIntersectedWithViewport) {
+    mLayerFEState.cursorFrame = Rect{3000, 3000, 3016, 3016};
+
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(1920, 1080)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCRotatedByTransform) {
+    mOutputState.transform = ui::Transform{TR_ROT_90};
+
+    EXPECT_CALL(*mHwcLayer, setCursorPosition(-4, 1)).WillOnce(Return(kDefaultError));
+
+    mOutputLayer.writeCursorPositionToHWC();
+}
+
+/*
+ * OutputLayer::getHwcLayer()
+ */
+
+TEST_F(OutputLayerTest, getHwcLayerHandlesNoHwcState) {
+    mOutputLayer.editState().hwc.reset();
+
+    EXPECT_TRUE(mOutputLayer.getHwcLayer() == nullptr);
+}
+
+TEST_F(OutputLayerTest, getHwcLayerHandlesNoHwcLayer) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+
+    EXPECT_TRUE(mOutputLayer.getHwcLayer() == nullptr);
+}
+
+TEST_F(OutputLayerTest, getHwcLayerReturnsHwcLayer) {
+    auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>();
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{hwcLayer};
+
+    EXPECT_EQ(hwcLayer.get(), mOutputLayer.getHwcLayer());
+}
+
+/*
+ * OutputLayer::requiresClientComposition()
+ */
+
+TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfNoHWC2State) {
+    mOutputLayer.editState().hwc.reset();
+
+    EXPECT_TRUE(mOutputLayer.requiresClientComposition());
+}
+
+TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfSetToClientComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+
+    EXPECT_TRUE(mOutputLayer.requiresClientComposition());
+}
+
+TEST_F(OutputLayerTest, requiresClientCompositionReturnsFalseIfSetToDeviceComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    EXPECT_FALSE(mOutputLayer.requiresClientComposition());
+}
+
+/*
+ * OutputLayer::isHardwareCursor()
+ */
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfNoHWC2State) {
+    mOutputLayer.editState().hwc.reset();
+
+    EXPECT_FALSE(mOutputLayer.isHardwareCursor());
+}
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsTrueIfSetToCursorComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CURSOR;
+
+    EXPECT_TRUE(mOutputLayer.isHardwareCursor());
+}
+
+TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfSetToDeviceComposition) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    EXPECT_FALSE(mOutputLayer.isHardwareCursor());
+}
+
+/*
+ * OutputLayer::applyDeviceCompositionTypeChange()
+ */
+
+TEST_F(OutputLayerTest, applyDeviceCompositionTypeChangeSetsNewType) {
+    mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
+    mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+
+    mOutputLayer.applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT);
+
+    ASSERT_TRUE(mOutputLayer.getState().hwc);
+    EXPECT_EQ(Hwc2::IComposerClient::Composition::CLIENT,
+              mOutputLayer.getState().hwc->hwcCompositionType);
+}
+
+/*
+ * OutputLayer::prepareForDeviceLayerRequests()
+ */
+
+TEST_F(OutputLayerTest, prepareForDeviceLayerRequestsResetsRequestState) {
+    mOutputLayer.editState().clearClientTarget = true;
+
+    mOutputLayer.prepareForDeviceLayerRequests();
+
+    EXPECT_FALSE(mOutputLayer.getState().clearClientTarget);
+}
+
+/*
+ * OutputLayer::applyDeviceLayerRequest()
+ */
+
+TEST_F(OutputLayerTest, applyDeviceLayerRequestHandlesClearClientTarget) {
+    mOutputLayer.editState().clearClientTarget = false;
+
+    mOutputLayer.applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET);
+
+    EXPECT_TRUE(mOutputLayer.getState().clearClientTarget);
+}
+
+TEST_F(OutputLayerTest, applyDeviceLayerRequestHandlesUnknownRequest) {
+    mOutputLayer.editState().clearClientTarget = false;
+
+    mOutputLayer.applyDeviceLayerRequest(static_cast<Hwc2::IComposerClient::LayerRequest>(0));
+
+    EXPECT_FALSE(mOutputLayer.getState().clearClientTarget);
+}
+
+/*
+ * OutputLayer::needsFiltering()
+ */
+
+TEST_F(OutputLayerTest, needsFilteringReturnsFalseIfDisplaySizeSameAsSourceSize) {
+    mOutputLayer.editState().displayFrame = Rect(100, 100, 200, 200);
+    mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.f, 100.f};
+
+    EXPECT_FALSE(mOutputLayer.needsFiltering());
+}
+
+TEST_F(OutputLayerTest, needsFilteringReturnsTrueIfDisplaySizeDifferentFromSourceSize) {
+    mOutputLayer.editState().displayFrame = Rect(100, 100, 200, 200);
+    mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.1f, 100.1f};
+
+    EXPECT_TRUE(mOutputLayer.needsFiltering());
+}
+
 } // namespace
 } // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index fee0c11..be0e9e4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -16,49 +16,172 @@
 
 #include <cmath>
 
+#include <android-base/stringprintf.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/impl/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <compositionengine/mock/CompositionEngine.h>
 #include <compositionengine/mock/DisplayColorProfile.h>
-#include <compositionengine/mock/Layer.h>
 #include <compositionengine/mock/LayerFE.h>
 #include <compositionengine/mock/OutputLayer.h>
 #include <compositionengine/mock/RenderSurface.h>
 #include <gtest/gtest.h>
+#include <renderengine/mock/RenderEngine.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
 
+#include "CallOrderStateMachineHelper.h"
+#include "MockHWC2.h"
 #include "RegionMatcher.h"
-#include "TransformMatcher.h"
 
 namespace android::compositionengine {
 namespace {
 
+using testing::_;
+using testing::ByMove;
+using testing::ByRef;
+using testing::DoAll;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::Eq;
+using testing::InSequence;
+using testing::Invoke;
+using testing::IsEmpty;
+using testing::Mock;
+using testing::Pointee;
+using testing::Property;
+using testing::Ref;
 using testing::Return;
 using testing::ReturnRef;
+using testing::SetArgPointee;
 using testing::StrictMock;
 
-class OutputTest : public testing::Test {
-public:
-    OutputTest() {
-        mOutput.setDisplayColorProfileForTest(
-                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
-        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+constexpr auto TR_IDENT = 0u;
+constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90;
+constexpr auto MAX_CLIENT_COMPOSITION_CACHE_SIZE = 3;
 
-        mOutput.editState().bounds = kDefaultDisplaySize;
+const mat4 kIdentity;
+const mat4 kNonIdentityHalf = mat4() * 0.5f;
+const mat4 kNonIdentityQuarter = mat4() * 0.25f;
+
+constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting =
+        static_cast<OutputColorSetting>(0x100);
+
+struct OutputPartialMockBase : public impl::Output {
+    // compositionengine::Output overrides
+    const OutputCompositionState& getState() const override { return mState; }
+    OutputCompositionState& editState() override { return mState; }
+
+    // Use mocks for all the remaining virtual functions
+    // not implemented by the base implementation class.
+    MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+    MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, compositionengine::OutputLayer*(size_t));
+    MOCK_METHOD2(ensureOutputLayer,
+                 compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+    MOCK_METHOD0(finalizePendingOutputLayers, void());
+    MOCK_METHOD0(clearOutputLayers, void());
+    MOCK_CONST_METHOD1(dumpState, void(std::string&));
+    MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&());
+    MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
+    MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+
+    impl::OutputCompositionState mState;
+};
+
+struct InjectedLayer {
+    InjectedLayer() {
+        EXPECT_CALL(*outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get()));
+        EXPECT_CALL(*outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+        EXPECT_CALL(*outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+
+        EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
     }
-    ~OutputTest() override = default;
+
+    mock::OutputLayer* outputLayer = {new StrictMock<mock::OutputLayer>};
+    sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>();
+    LayerFECompositionState layerFEState;
+    impl::OutputLayerCompositionState outputLayerState;
+};
+
+struct NonInjectedLayer {
+    NonInjectedLayer() {
+        EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(*layerFE.get()));
+        EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+        EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+
+        EXPECT_CALL(*layerFE, getCompositionState()).WillRepeatedly(Return(&layerFEState));
+    }
+
+    mock::OutputLayer outputLayer;
+    sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>();
+    LayerFECompositionState layerFEState;
+    impl::OutputLayerCompositionState outputLayerState;
+};
+
+struct OutputTest : public testing::Test {
+    class Output : public impl::Output {
+    public:
+        using impl::Output::injectOutputLayerForTest;
+        virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+    };
+
+    static std::shared_ptr<Output> createOutput(
+            const compositionengine::CompositionEngine& compositionEngine) {
+        return impl::createOutputTemplated<Output>(compositionEngine);
+    }
+
+    OutputTest() {
+        mOutput->setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+        mOutput->editState().bounds = kDefaultDisplaySize;
+    }
+
+    void injectOutputLayer(InjectedLayer& layer) {
+        mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(layer.outputLayer));
+    }
+
+    void injectNullOutputLayer() {
+        mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(nullptr));
+    }
 
     static const Rect kDefaultDisplaySize;
 
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
     mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
-    impl::Output mOutput{mCompositionEngine};
+    std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine);
 };
 
 const Rect OutputTest::kDefaultDisplaySize{100, 200};
 
-/* ------------------------------------------------------------------------
+using ColorProfile = compositionengine::Output::ColorProfile;
+
+void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) {
+    android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name,
+                                 toString(profile.mode).c_str(), profile.mode,
+                                 toString(profile.dataspace).c_str(), profile.dataspace,
+                                 toString(profile.renderIntent).c_str(), profile.renderIntent,
+                                 toString(profile.colorSpaceAgnosticDataspace).c_str(),
+                                 profile.colorSpaceAgnosticDataspace);
+}
+
+// Checks for a ColorProfile match
+MATCHER_P(ColorProfileEq, expected, "") {
+    std::string buf;
+    buf.append("ColorProfiles are not equal\n");
+    dumpColorProfile(expected, buf, "expected value");
+    dumpColorProfile(arg, buf, "actual value");
+    *result_listener << buf;
+
+    return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) &&
+            (expected.renderIntent == arg.renderIntent) &&
+            (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace);
+}
+
+/*
  * Basic construction
  */
 
@@ -67,48 +190,48 @@
     EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
     EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true));
 
-    EXPECT_TRUE(mOutput.isValid());
+    EXPECT_TRUE(mOutput->isValid());
 
     // If we take away the required components, it is no longer valid.
-    mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
+    mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>());
 
     EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true));
 
-    EXPECT_FALSE(mOutput.isValid());
+    EXPECT_FALSE(mOutput->isValid());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setCompositionEnabled()
  */
 
 TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
-    mOutput.editState().isEnabled = true;
+    mOutput->editState().isEnabled = true;
 
-    mOutput.setCompositionEnabled(true);
+    mOutput->setCompositionEnabled(true);
 
-    EXPECT_TRUE(mOutput.getState().isEnabled);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+    EXPECT_TRUE(mOutput->getState().isEnabled);
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
 }
 
 TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
-    mOutput.editState().isEnabled = false;
+    mOutput->editState().isEnabled = false;
 
-    mOutput.setCompositionEnabled(true);
+    mOutput->setCompositionEnabled(true);
 
-    EXPECT_TRUE(mOutput.getState().isEnabled);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_TRUE(mOutput->getState().isEnabled);
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
 TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
-    mOutput.editState().isEnabled = true;
+    mOutput->editState().isEnabled = true;
 
-    mOutput.setCompositionEnabled(false);
+    mOutput->setCompositionEnabled(false);
 
-    EXPECT_FALSE(mOutput.getState().isEnabled);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_FALSE(mOutput->getState().isEnabled);
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setProjection()
  */
 
@@ -117,20 +240,23 @@
     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 Rect sourceClip{9, 10, 11, 12};
+    const Rect destinationClip{13, 14, 15, 16};
     const bool needsFiltering = true;
 
-    mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+    mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip,
+                           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);
+    EXPECT_THAT(mOutput->getState().transform, transform);
+    EXPECT_EQ(orientation, mOutput->getState().orientation);
+    EXPECT_EQ(frame, mOutput->getState().frame);
+    EXPECT_EQ(viewport, mOutput->getState().viewport);
+    EXPECT_EQ(sourceClip, mOutput->getState().sourceClip);
+    EXPECT_EQ(destinationClip, mOutput->getState().destinationClip);
+    EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setBounds()
  */
 
@@ -140,94 +266,160 @@
     EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1);
     EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize));
 
-    mOutput.setBounds(displaySize);
+    mOutput->setBounds(displaySize);
 
-    EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds);
+    EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds);
 
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize))));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setLayerStackFilter()
  */
 
 TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
     const uint32_t layerStack = 123u;
-    mOutput.setLayerStackFilter(layerStack, true);
+    mOutput->setLayerStackFilter(layerStack, true);
 
-    EXPECT_TRUE(mOutput.getState().layerStackInternal);
-    EXPECT_EQ(layerStack, mOutput.getState().layerStackId);
+    EXPECT_TRUE(mOutput->getState().layerStackInternal);
+    EXPECT_EQ(layerStack, mOutput->getState().layerStackId);
 
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setColorTransform
  */
 
-TEST_F(OutputTest, setColorTransformSetsTransform) {
-    // Identity matrix sets an identity state value
-    const mat4 identity;
+TEST_F(OutputTest, setColorTransformWithNoChangeFlaggedSkipsUpdates) {
+    mOutput->editState().colorTransformMatrix = kIdentity;
 
-    mOutput.setColorTransform(identity);
+    // If no colorTransformMatrix is set the update should be skipped.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = std::nullopt;
 
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
-    EXPECT_EQ(identity, mOutput.getState().colorTransformMat);
+    mOutput->setColorTransform(refreshArgs);
 
-    // Since identity is the default, the dirty region should be unchanged (empty)
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+    // The internal state should be unchanged
+    EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
 
-    // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentityHalf = mat4() * 0.5;
-
-    mOutput.setColorTransform(nonIdentityHalf);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
-    EXPECT_EQ(nonIdentityHalf, mOutput.getState().colorTransformMat);
-
-    // Since this is a state change, the entire output should now be dirty.
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
-
-    // Non-identity matrix sets a non-identity state value
-    const mat4 nonIdentityQuarter = mat4() * 0.25;
-
-    mOutput.setColorTransform(nonIdentityQuarter);
-
-    EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
-    EXPECT_EQ(nonIdentityQuarter, mOutput.getState().colorTransformMat);
-
-    // Since this is a state change, the entire output should now be dirty.
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    // No dirty region should be set
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
 }
 
-/* ------------------------------------------------------------------------
- * Output::setColorMode
+TEST_F(OutputTest, setColorTransformWithNoActualChangeSkipsUpdates) {
+    mOutput->editState().colorTransformMatrix = kIdentity;
+
+    // Attempting to set the same colorTransformMatrix that is already set should
+    // also skip the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kIdentity;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should be unchanged
+    EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
+
+    // No dirty region should be set
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateToIdentity) {
+    mOutput->editState().colorTransformMatrix = kNonIdentityHalf;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kIdentity;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateForIdentityToHalf) {
+    mOutput->editState().colorTransformMatrix = kIdentity;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kNonIdentityHalf;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kNonIdentityHalf, mOutput->getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+TEST_F(OutputTest, setColorTransformPerformsUpdateForHalfToQuarter) {
+    mOutput->editState().colorTransformMatrix = kNonIdentityHalf;
+
+    // Setting a different colorTransformMatrix should perform the update.
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.colorTransformMatrix = kNonIdentityQuarter;
+
+    mOutput->setColorTransform(refreshArgs);
+
+    // The internal state should have been updated
+    EXPECT_EQ(kNonIdentityQuarter, mOutput->getState().colorTransformMatrix);
+
+    // The dirtyRegion should be set to the full display size
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+}
+
+/*
+ * Output::setColorProfile
  */
 
-TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) {
+using OutputSetColorProfileTest = OutputTest;
+
+TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
+    using ColorProfile = Output::ColorProfile;
+
+    EXPECT_CALL(*mDisplayColorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
     EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1);
 
-    mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                          ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+                                          ui::Dataspace::UNKNOWN});
 
-    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput.getState().colorMode);
-    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput.getState().dataspace);
-    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
+    EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode);
+    EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace);
+    EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent);
+    EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace);
+
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
 }
 
-TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
-    mOutput.editState().colorMode = ui::ColorMode::DISPLAY_P3;
-    mOutput.editState().dataspace = ui::Dataspace::DISPLAY_P3;
-    mOutput.editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) {
+    using ColorProfile = Output::ColorProfile;
 
-    mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
-                         ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+    EXPECT_CALL(*mDisplayColorProfile,
+                getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                   ui::Dataspace::UNKNOWN))
+            .WillOnce(Return(ui::Dataspace::UNKNOWN));
 
-    EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+    mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3;
+    mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3;
+    mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC;
+    mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN;
+
+    mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3,
+                                          ui::RenderIntent::TONE_MAP_COLORIMETRIC,
+                                          ui::Dataspace::UNKNOWN});
+
+    EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region()));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::setRenderSurface()
  */
 
@@ -237,22 +429,22 @@
     mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
     EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize));
 
-    mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
+    mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface));
 
-    EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds);
+    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);
+    mOutput->editState().viewport = viewport;
+    mOutput->editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getDirtyRegion(true);
+        Region result = mOutput->getDirtyRegion(true);
 
         EXPECT_THAT(result, RegionEq(Region(viewport)));
     }
@@ -260,18 +452,18 @@
 
 TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) {
     const Rect viewport{100, 200};
-    mOutput.editState().viewport = viewport;
-    mOutput.editState().dirtyRegion.set(50, 300);
+    mOutput->editState().viewport = viewport;
+    mOutput->editState().dirtyRegion.set(50, 300);
 
     {
-        Region result = mOutput.getDirtyRegion(false);
+        Region result = mOutput->getDirtyRegion(false);
 
         // The dirtyRegion should be clipped to the display bounds.
         EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
     }
 }
 
-/* ------------------------------------------------------------------------
+/*
  * Output::belongsInOutput()
  */
 
@@ -280,101 +472,3574 @@
     const uint32_t layerStack2 = 456u;
 
     // If the output accepts layerStack1 and internal-only layers....
-    mOutput.setLayerStackFilter(layerStack1, true);
+    mOutput->setLayerStackFilter(layerStack1, true);
+
+    // A layer with no layerStack does not belong to it, internal-only or not.
+    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false));
+    EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, 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));
+    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);
+    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));
+    EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false));
 }
 
-/* ------------------------------------------------------------------------
+TEST_F(OutputTest, belongsInOutputHandlesLayerWithNoCompositionState) {
+    NonInjectedLayer layer;
+    sp<LayerFE> layerFE(layer.layerFE);
+
+    // If the layer has no composition state, it does not belong to any output.
+    EXPECT_CALL(*layer.layerFE, getCompositionState).WillOnce(Return(nullptr));
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+}
+
+TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) {
+    NonInjectedLayer layer;
+    sp<LayerFE> layerFE(layer.layerFE);
+
+    const uint32_t layerStack1 = 123u;
+    const uint32_t layerStack2 = 456u;
+
+    // If the output accepts layerStack1 and internal-only layers....
+    mOutput->setLayerStackFilter(layerStack1, true);
+
+    // A layer with no layerStack does not belong to it, internal-only or not.
+    layer.layerFEState.layerStackId = std::nullopt;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = std::nullopt;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    // Any layer with layerStack1 belongs to it, internal-only or not.
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    // If the output accepts layerStack1 but not internal-only layers...
+    mOutput->setLayerStackFilter(layerStack1, false);
+
+    // Only non-internal layers with layerStack1 belong to it.
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_TRUE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack1;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = true;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+
+    layer.layerFEState.layerStackId = layerStack2;
+    layer.layerFEState.internalOnly = false;
+    EXPECT_FALSE(mOutput->belongsInOutput(layerFE));
+}
+
+/*
  * Output::getOutputLayerForLayer()
  */
 
 TEST_F(OutputTest, getOutputLayerForLayerWorks) {
-    mock::OutputLayer* outputLayer1 = new StrictMock<mock::OutputLayer>();
-    mock::OutputLayer* outputLayer2 = new StrictMock<mock::OutputLayer>();
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    NonInjectedLayer layer3;
 
-    Output::OutputLayers outputLayers;
-    outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer1));
-    outputLayers.emplace_back(nullptr);
-    outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer2));
-    mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
-
-    StrictMock<mock::Layer> layer;
-    StrictMock<mock::Layer> otherLayer;
+    injectOutputLayer(layer1);
+    injectNullOutputLayer();
+    injectOutputLayer(layer2);
 
     // If the input layer matches the first OutputLayer, it will be returned.
-    EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(layer));
-    EXPECT_EQ(outputLayer1, mOutput.getOutputLayerForLayer(&layer));
+    EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+    EXPECT_EQ(layer1.outputLayer, mOutput->getOutputLayerForLayer(layer1.layerFE));
 
     // If the input layer matches the second OutputLayer, it will be returned.
-    EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
-    EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(layer));
-    EXPECT_EQ(outputLayer2, mOutput.getOutputLayerForLayer(&layer));
+    EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+    EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get()));
+    EXPECT_EQ(layer2.outputLayer, mOutput->getOutputLayerForLayer(layer2.layerFE));
 
     // If the input layer does not match an output layer, null will be returned.
-    EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer));
-    EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(otherLayer));
-    EXPECT_EQ(nullptr, mOutput.getOutputLayerForLayer(&layer));
+    EXPECT_CALL(*layer1.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer1.layerFE.get()));
+    EXPECT_CALL(*layer2.outputLayer, getLayerFE()).WillOnce(ReturnRef(*layer2.layerFE.get()));
+    EXPECT_EQ(nullptr, mOutput->getOutputLayerForLayer(layer3.layerFE));
 }
 
-/* ------------------------------------------------------------------------
- * Output::getOrCreateOutputLayer()
+/*
+ * Output::setReleasedLayers()
  */
 
-TEST_F(OutputTest, getOrCreateOutputLayerWorks) {
-    mock::OutputLayer* existingOutputLayer = new StrictMock<mock::OutputLayer>();
+using OutputSetReleasedLayersTest = OutputTest;
 
-    Output::OutputLayers outputLayers;
-    outputLayers.emplace_back(nullptr);
-    outputLayers.emplace_back(std::unique_ptr<OutputLayer>(existingOutputLayer));
-    mOutput.setOutputLayersOrderedByZ(std::move(outputLayers));
+TEST_F(OutputSetReleasedLayersTest, setReleasedLayersTakesGivenLayers) {
+    sp<StrictMock<mock::LayerFE>> layer1FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> layer2FE{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> layer3FE{new StrictMock<mock::LayerFE>()};
 
-    std::shared_ptr<mock::Layer> layer{new StrictMock<mock::Layer>()};
-    sp<LayerFE> layerFE{new StrictMock<mock::LayerFE>()};
+    Output::ReleasedLayers layers;
+    layers.push_back(layer1FE);
+    layers.push_back(layer2FE);
+    layers.push_back(layer3FE);
 
-    StrictMock<mock::Layer> otherLayer;
+    mOutput->setReleasedLayers(std::move(layers));
 
-    {
-        // If there is no OutputLayer corresponding to the input layer, a
-        // new OutputLayer is constructed and returned.
-        EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(otherLayer));
-        auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE);
-        EXPECT_NE(existingOutputLayer, result.get());
-        EXPECT_TRUE(result.get() != nullptr);
-        EXPECT_EQ(layer.get(), &result->getLayer());
-        EXPECT_EQ(layerFE.get(), &result->getLayerFE());
+    const auto& setLayers = mOutput->getReleasedLayersForTest();
+    ASSERT_EQ(3u, setLayers.size());
+    ASSERT_EQ(layer1FE.get(), setLayers[0].promote().get());
+    ASSERT_EQ(layer2FE.get(), setLayers[1].promote().get());
+    ASSERT_EQ(layer3FE.get(), setLayers[2].promote().get());
+}
 
-        // The entries in the ordered array should be unchanged.
-        auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
-        EXPECT_EQ(nullptr, outputLayers[0].get());
-        EXPECT_EQ(existingOutputLayer, outputLayers[1].get());
+/*
+ * Output::updateLayerStateFromFE()
+ */
+
+using OutputUpdateLayerStateFromFETest = OutputTest;
+
+TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
+    CompositionRefreshArgs refreshArgs;
+
+    mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, preparesContentStateForAllContainedLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+    EXPECT_CALL(*layer2.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+    EXPECT_CALL(*layer3.layerFE.get(), prepareCompositionState(LayerFE::StateSubset::Content));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.updatingGeometryThisFrame = false;
+
+    mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, preparesGeometryAndContentStateForAllContainedLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+    EXPECT_CALL(*layer2.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+    EXPECT_CALL(*layer3.layerFE, prepareCompositionState(LayerFE::StateSubset::GeometryAndContent));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    CompositionRefreshArgs refreshArgs;
+    refreshArgs.updatingGeometryThisFrame = true;
+
+    mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+/*
+ * Output::updateAndWriteCompositionState()
+ */
+
+using OutputUpdateAndWriteCompositionStateTest = OutputTest;
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfLayers) {
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    mOutput->editState().isEnabled = false;
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    CompositionRefreshArgs args;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(true));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(true));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(true));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = true;
+    args.devOptForceClientComposition = false;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = true;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+/*
+ * Output::prepareFrame()
+ */
+
+struct OutputPrepareFrameTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD0(chooseCompositionStrategy, void());
+    };
+
+    OutputPrepareFrameTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
     }
 
-    {
-        // If there is an existing OutputLayer for the requested layer, an owned
-        // pointer is returned
-        EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(*layer));
-        auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE);
-        EXPECT_EQ(existingOutputLayer, result.get());
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+};
 
-        // The corresponding entry in the ordered array should be cleared.
-        auto& outputLayers = mOutput.getOutputLayersOrderedByZ();
-        EXPECT_EQ(nullptr, outputLayers[0].get());
-        EXPECT_EQ(nullptr, outputLayers[1].get());
+TEST_F(OutputPrepareFrameTest, takesEarlyOutIfNotEnabled) {
+    mOutput.editState().isEnabled = false;
+
+    mOutput.prepareFrame();
+}
+
+TEST_F(OutputPrepareFrameTest, delegatesToChooseCompositionStrategyAndRenderSurface) {
+    mOutput.editState().isEnabled = true;
+    mOutput.editState().usesClientComposition = false;
+    mOutput.editState().usesDeviceComposition = true;
+
+    EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1);
+    EXPECT_CALL(*mRenderSurface, prepareFrame(false, true));
+
+    mOutput.prepareFrame();
+}
+
+// Note: Use OutputTest and not OutputPrepareFrameTest, so the real
+// base chooseCompositionStrategy() is invoked.
+TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) {
+    mOutput->editState().isEnabled = true;
+    mOutput->editState().usesClientComposition = false;
+    mOutput->editState().usesDeviceComposition = true;
+
+    EXPECT_CALL(*mRenderSurface, prepareFrame(true, false));
+
+    mOutput->prepareFrame();
+
+    EXPECT_TRUE(mOutput->getState().usesClientComposition);
+    EXPECT_FALSE(mOutput->getState().usesDeviceComposition);
+}
+
+/*
+ * Output::prepare()
+ */
+
+struct OutputPrepareTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(rebuildLayerStacks,
+                     void(const compositionengine::CompositionRefreshArgs&,
+                          compositionengine::LayerFESet&));
+    };
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+};
+
+TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+    InSequence seq;
+    EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+
+    mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+/*
+ * Output::rebuildLayerStacks()
+ */
+
+struct OutputRebuildLayerStacksTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(collectVisibleLayers,
+                     void(const compositionengine::CompositionRefreshArgs&,
+                          compositionengine::Output::CoverageState&));
+    };
+
+    OutputRebuildLayerStacksTest() {
+        mOutput.mState.isEnabled = true;
+        mOutput.mState.transform = kIdentityTransform;
+        mOutput.mState.bounds = kOutputBounds;
+
+        mRefreshArgs.updatingOutputGeometryThisFrame = true;
+
+        mCoverageAboveCoveredLayersToSet = Region(Rect(0, 0, 10, 10));
+
+        EXPECT_CALL(mOutput, collectVisibleLayers(Ref(mRefreshArgs), _))
+                .WillRepeatedly(Invoke(this, &OutputRebuildLayerStacksTest::setTestCoverageValues));
     }
+
+    void setTestCoverageValues(const CompositionRefreshArgs&,
+                               compositionengine::Output::CoverageState& state) {
+        state.aboveCoveredLayers = mCoverageAboveCoveredLayersToSet;
+        state.aboveOpaqueLayers = mCoverageAboveOpaqueLayersToSet;
+        state.dirtyRegion = mCoverageDirtyRegionToSet;
+    }
+
+    static const ui::Transform kIdentityTransform;
+    static const ui::Transform kRotate90Transform;
+    static const Rect kOutputBounds;
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+    Region mCoverageAboveCoveredLayersToSet;
+    Region mCoverageAboveOpaqueLayersToSet;
+    Region mCoverageDirtyRegionToSet;
+};
+
+const ui::Transform OutputRebuildLayerStacksTest::kIdentityTransform{TR_IDENT, 1920, 1080};
+const ui::Transform OutputRebuildLayerStacksTest::kRotate90Transform{TR_ROT_90, 1920, 1080};
+const Rect OutputRebuildLayerStacksTest::kOutputBounds{0, 0, 1920, 1080};
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotEnabled) {
+    mOutput.mState.isEnabled = false;
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotUpdatingGeometryThisFrame) {
+    mRefreshArgs.updatingOutputGeometryThisFrame = false;
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndFullCoverage) {
+    mOutput.mState.transform = kIdentityTransform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1920, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndPartialCoverage) {
+    mOutput.mState.transform = kIdentityTransform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 960, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(960, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndFullCoverage) {
+    mOutput.mState.transform = kRotate90Transform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 1920));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndPartialCoverage) {
+    mOutput.mState.transform = kRotate90Transform;
+
+    mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 960));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 960, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWithNoRotation) {
+    mOutput.mState.transform = kIdentityTransform;
+    mOutput.mState.dirtyRegion = Region(Rect(960, 0, 1920, 1080));
+
+    mCoverageDirtyRegionToSet = Region(Rect(0, 0, 960, 1080));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWith90Rotation) {
+    mOutput.mState.transform = kRotate90Transform;
+    mOutput.mState.dirtyRegion = Region(Rect(0, 960, 1080, 1920));
+
+    mCoverageDirtyRegionToSet = Region(Rect(0, 0, 1080, 960));
+
+    mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+    EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1080, 1920))));
+}
+
+/*
+ * Output::collectVisibleLayers()
+ */
+
+struct OutputCollectVisibleLayersTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD2(ensureOutputLayerIfVisible,
+                     void(sp<compositionengine::LayerFE>&,
+                          compositionengine::Output::CoverageState&));
+        MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(finalizePendingOutputLayers, void());
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+            EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+        }
+
+        StrictMock<mock::OutputLayer> outputLayer;
+        impl::OutputLayerCompositionState outputLayerState;
+        sp<StrictMock<mock::LayerFE>> layerFE{new StrictMock<mock::LayerFE>()};
+    };
+
+    OutputCollectVisibleLayersTest() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mLayer1.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mLayer2.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+                .WillRepeatedly(Return(&mLayer3.outputLayer));
+
+        mRefreshArgs.layers.push_back(mLayer1.layerFE);
+        mRefreshArgs.layers.push_back(mLayer2.layerFE);
+        mRefreshArgs.layers.push_back(mLayer3.layerFE);
+    }
+
+    StrictMock<OutputPartialMock> mOutput;
+    CompositionRefreshArgs mRefreshArgs;
+    LayerFESet mGeomSnapshots;
+    Output::CoverageState mCoverageState{mGeomSnapshots};
+    Layer mLayer1;
+    Layer mLayer2;
+    Layer mLayer3;
+};
+
+TEST_F(OutputCollectVisibleLayersTest, doesMinimalWorkIfNoLayers) {
+    mRefreshArgs.layers.clear();
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+
+    EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+    EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+    mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+}
+
+TEST_F(OutputCollectVisibleLayersTest, processesCandidateLayersReversedAndSetsOutputLayerZ) {
+    // Enforce a call order sequence for this test.
+    InSequence seq;
+
+    // Layer coverage is evaluated from front to back!
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layerFE), Ref(mCoverageState)));
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layerFE), Ref(mCoverageState)));
+    EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layerFE), Ref(mCoverageState)));
+
+    EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+    EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+    mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+
+    // Ensure all output layers have been assigned a simple/flattened z-order.
+    EXPECT_EQ(0u, mLayer1.outputLayerState.z);
+    EXPECT_EQ(1u, mLayer2.outputLayerState.z);
+    EXPECT_EQ(2u, mLayer3.outputLayerState.z);
+}
+
+/*
+ * Output::ensureOutputLayerIfVisible()
+ */
+
+struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD1(belongsInOutput, bool(const sp<compositionengine::LayerFE>&));
+        MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
+        MOCK_METHOD2(ensureOutputLayer,
+                     compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+    };
+
+    OutputEnsureOutputLayerIfVisibleTest() {
+        EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE)))
+                .WillRepeatedly(Return(true));
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer.outputLayer));
+
+        mOutput.mState.bounds = Rect(0, 0, 200, 300);
+        mOutput.mState.viewport = Rect(0, 0, 200, 300);
+        mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
+
+        mLayer.layerFEState.isVisible = true;
+        mLayer.layerFEState.isOpaque = true;
+        mLayer.layerFEState.contentDirty = true;
+        mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+        mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+        mLayer.layerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100));
+
+        mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200));
+        mLayer.outputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200));
+
+        mGeomSnapshots.insert(mLayer.layerFE);
+    }
+
+    void ensureOutputLayerIfVisible() {
+        sp<LayerFE> layerFE(mLayer.layerFE);
+        mOutput.ensureOutputLayerIfVisible(layerFE, mCoverageState);
+    }
+
+    static const Region kEmptyRegion;
+    static const Region kFullBoundsNoRotation;
+    static const Region kRightHalfBoundsNoRotation;
+    static const Region kLowerHalfBoundsNoRotation;
+    static const Region kFullBounds90Rotation;
+
+    StrictMock<OutputPartialMock> mOutput;
+    LayerFESet mGeomSnapshots;
+    Output::CoverageState mCoverageState{mGeomSnapshots};
+
+    NonInjectedLayer mLayer;
+};
+
+const Region OutputEnsureOutputLayerIfVisibleTest::kEmptyRegion = Region(Rect(0, 0, 0, 0));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBoundsNoRotation =
+        Region(Rect(0, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kRightHalfBoundsNoRotation =
+        Region(Rect(0, 100, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation =
+        Region(Rect(50, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
+        Region(Rect(0, 0, 200, 100));
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
+    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+    EXPECT_CALL(*mLayer.layerFE,
+                prepareCompositionState(compositionengine::LayerFE::StateSubset::BasicGeometry));
+
+    mGeomSnapshots.clear();
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
+    EXPECT_CALL(mOutput, belongsInOutput(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false));
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasNoCompositionState) {
+    EXPECT_CALL(*mLayer.layerFE, getCompositionState()).WillOnce(Return(nullptr));
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerNotVisible) {
+    mLayer.layerFEState.isVisible = false;
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisibleRegion) {
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0};
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
+    mOutput.mState.bounds = Rect(0, 0, 0, 0);
+
+    ensureOutputLayerIfVisible();
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kRightHalfBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kRightHalfBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = false;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = false;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kLowerHalfBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyRotated90Layer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+    mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+    mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyRotated90Layer) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+    mLayer.outputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+    mLayer.outputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mOutput.mState.viewport = Rect(0, 0, 300, 200);
+    mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kFullBoundsNoRotation));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+       handlesCreatingOutputLayerForOpaqueDirtyArbitraryTransformLayer) {
+    ui::Transform arbitraryTransform;
+    arbitraryTransform.set(1, 1, -1, 1);
+    arbitraryTransform.set(0, 100);
+
+    mLayer.layerFEState.isOpaque = true;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+    mLayer.layerFEState.geomLayerTransform = arbitraryTransform;
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kRegion = Region(Rect(0, 0, 300, 300));
+    const Region kRegionClipped = Region(Rect(0, 0, 200, 300));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kRegion));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion, RegionEq(kRegion));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesTest) {
+    mLayer.layerFEState.isOpaque = false;
+    mLayer.layerFEState.contentDirty = true;
+    mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    mCoverageState.aboveCoveredLayers = Region(Rect(50, 0, 150, 200));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(50, 0, 150, 200));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+    const Region kExpectedAboveCoveredRegion = Region(Rect(0, 0, 150, 200));
+    const Region kExpectedAboveOpaqueRegion = Region(Rect(50, 0, 150, 200));
+    const Region kExpectedLayerVisibleRegion = Region(Rect(0, 0, 50, 200));
+    const Region kExpectedLayerCoveredRegion = Region(Rect(50, 0, 100, 200));
+    const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(0, 100, 50, 200));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion,
+                RegionEq(kExpectedLayerVisibleRegion));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesWithShadowsTest) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayer.layerFEState.geomLayerTransform = translate;
+    mLayer.layerFEState.shadowRadius = 10.0f;
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    // half of the layer including the casting shadow is covered and opaque
+    mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 100, 260));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 100, 260));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+    const Region kExpectedAboveCoveredRegion = Region(Rect(40, 40, 160, 260));
+    // add starting opaque region to the opaque half of the casting layer bounds
+    const Region kExpectedAboveOpaqueRegion =
+            Region(Rect(40, 40, 100, 260)).orSelf(Rect(100, 50, 150, 250));
+    const Region kExpectedLayerVisibleRegion = Region(Rect(100, 40, 160, 260));
+    const Region kExpectedoutputSpaceLayerVisibleRegion = Region(Rect(100, 50, 150, 250));
+    const Region kExpectedLayerCoveredRegion = Region(Rect(40, 40, 100, 260));
+    const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(100, 40, 160, 260));
+    const Region kExpectedLayerShadowRegion =
+            Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+    EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+    EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+    EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.visibleNonTransparentRegion,
+                RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+    EXPECT_THAT(mLayer.outputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+    EXPECT_THAT(mLayer.outputLayerState.outputSpaceVisibleRegion,
+                RegionEq(kExpectedoutputSpaceLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+    EXPECT_FALSE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, shadowRegionOnlyTest) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayer.layerFEState.geomLayerTransform = translate;
+    mLayer.layerFEState.shadowRadius = 10.0f;
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    // Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
+    mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 150, 260));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 150, 260));
+
+    EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE)))
+            .WillOnce(Return(&mLayer.outputLayer));
+
+    ensureOutputLayerIfVisible();
+
+    const Region kExpectedLayerVisibleRegion = Region(Rect(150, 40, 160, 260));
+    const Region kExpectedLayerShadowRegion =
+            Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+    EXPECT_THAT(mLayer.outputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+    EXPECT_THAT(mLayer.outputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+    EXPECT_TRUE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadowIsCovered) {
+    ui::Transform translate;
+    translate.set(50, 50);
+    mLayer.layerFEState.geomLayerTransform = translate;
+    mLayer.layerFEState.shadowRadius = 10.0f;
+
+    mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+    // Casting layer and its shadows are covered by an opaque region
+    mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 160, 260));
+    mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 160, 260));
+
+    ensureOutputLayerIfVisible();
+}
+
+/*
+ * Output::present()
+ */
+
+struct OutputPresentTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(updateAndWriteCompositionState,
+                     void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(beginFrame, void());
+        MOCK_METHOD0(prepareFrame, void());
+        MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&));
+        MOCK_METHOD0(postFramebuffer, void());
+    };
+
+    StrictMock<OutputPartialMock> mOutput;
+};
+
+TEST_F(OutputPresentTest, justInvokesChildFunctionsInSequence) {
+    CompositionRefreshArgs args;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, updateColorProfile(Ref(args)));
+    EXPECT_CALL(mOutput, updateAndWriteCompositionState(Ref(args)));
+    EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
+    EXPECT_CALL(mOutput, beginFrame());
+    EXPECT_CALL(mOutput, prepareFrame());
+    EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
+    EXPECT_CALL(mOutput, finishFrame(Ref(args)));
+    EXPECT_CALL(mOutput, postFramebuffer());
+
+    mOutput.present(args);
+}
+
+/*
+ * Output::updateColorProfile()
+ */
+
+struct OutputUpdateColorProfileTest : public testing::Test {
+    using TestType = OutputUpdateColorProfileTest;
+
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::LayerFE> mLayerFE;
+        LayerFECompositionState mLayerFEState;
+    };
+
+    OutputUpdateColorProfileTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+                .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+                .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+                .WillRepeatedly(Return(&mLayer3.mOutputLayer));
+    }
+
+    struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+        void execute() { getInstance()->mOutput.updateColorProfile(getInstance()->mRefreshArgs); }
+    };
+
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+
+    Layer mLayer1;
+    Layer mLayer2;
+    Layer mLayer3;
+
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+// TODO(b/144522012): Refactor Output::updateColorProfile and the related code
+// to make it easier to write unit tests.
+
+TEST_F(OutputUpdateColorProfileTest, setsAColorProfileWhenUnmanaged) {
+    // When the outputColorSetting is set to kUnmanaged, the implementation sets
+    // a simple default color profile without looking at anything else.
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+    EXPECT_CALL(mOutput,
+                setColorProfile(ColorProfileEq(
+                        ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                                     ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN})));
+
+    mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged;
+    mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+    mOutput.updateColorProfile(mRefreshArgs);
+}
+
+struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile
+      : public OutputUpdateColorProfileTest {
+    OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+    }
+
+    struct ExpectBestColorModeCallResultUsedToSetColorProfileState
+          : public CallOrderStateMachineHelper<
+                    TestType, ExpectBestColorModeCallResultUsedToSetColorProfileState> {
+        [[nodiscard]] auto expectBestColorModeCallResultUsedToSetColorProfile(
+                ui::ColorMode colorMode, ui::Dataspace dataspace, ui::RenderIntent renderIntent) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _,
+                                         _))
+                    .WillOnce(DoAll(SetArgPointee<2>(dataspace), SetArgPointee<3>(colorMode),
+                                    SetArgPointee<4>(renderIntent)));
+            EXPECT_CALL(getInstance()->mOutput,
+                        setColorProfile(
+                                ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent,
+                                                            ui::Dataspace::UNKNOWN})));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() {
+        return ExpectBestColorModeCallResultUsedToSetColorProfileState::make(this);
+    }
+};
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+       Native_Unknown_Colorimetric_Set) {
+    verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::NATIVE,
+                                                                ui::Dataspace::UNKNOWN,
+                                                                ui::RenderIntent::COLORIMETRIC)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+       DisplayP3_DisplayP3_Enhance_Set) {
+    verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::DISPLAY_P3,
+                                                                ui::Dataspace::DISPLAY_P3,
+                                                                ui::RenderIntent::ENHANCE)
+            .execute();
+}
+
+struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
+      : public OutputUpdateColorProfileTest {
+    OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0u));
+        EXPECT_CALL(*mDisplayColorProfile,
+                    getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
+                .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
+                                      SetArgPointee<3>(ui::ColorMode::NATIVE),
+                                      SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC)));
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+    }
+
+    struct IfColorSpaceAgnosticDataspaceSetToState
+          : public CallOrderStateMachineHelper<TestType, IfColorSpaceAgnosticDataspaceSetToState> {
+        [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) {
+            getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace;
+            return nextState<ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState>();
+        }
+    };
+
+    struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState
+          : public CallOrderStateMachineHelper<
+                    TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> {
+        [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(
+                ui::Dataspace dataspace) {
+            EXPECT_CALL(getInstance()->mOutput,
+                        setColorProfile(ColorProfileEq(
+                                ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+                                             ui::RenderIntent::COLORIMETRIC, dataspace})));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) {
+    verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3)
+            .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) {
+    verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB)
+            .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB)
+            .execute();
+}
+
+struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
+      : public OutputUpdateColorProfileTest {
+    // Internally the implementation looks through the dataspaces of all the
+    // visible layers. The topmost one that also has an actual dataspace
+    // preference set is used to drive subsequent choices.
+
+    OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+    }
+
+    struct IfTopLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+        [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer3.mLayerFEState.dataspace = dataspace;
+            return nextState<AndIfMiddleLayerDataspaceState>();
+        }
+        [[nodiscard]] auto ifTopLayerHasNoPreference() {
+            return ifTopLayerIs(ui::Dataspace::UNKNOWN);
+        }
+    };
+
+    struct AndIfMiddleLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, AndIfMiddleLayerDataspaceState> {
+        [[nodiscard]] auto andIfMiddleLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+            return nextState<AndIfBottomLayerDataspaceState>();
+        }
+        [[nodiscard]] auto andIfMiddleLayerHasNoPreference() {
+            return andIfMiddleLayerIs(ui::Dataspace::UNKNOWN);
+        }
+    };
+
+    struct AndIfBottomLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+        [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+        [[nodiscard]] auto andIfBottomLayerHasNoPreference() {
+            return andIfBottomLayerIs(ui::Dataspace::UNKNOWN);
+        }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(dataspace, _, _, _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       noStrongLayerPrefenceUses_V0_SRGB) {
+    // If none of the layers indicate a preference, then V0_SRGB is the
+    // preferred choice (subject to additional checks).
+    verify().ifTopLayerHasNoPreference()
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerHasNoPreference()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifTopmostUses_DisplayP3_Then_DisplayP3_Chosen) {
+    // If only the topmost layer has a preference, then that is what is chosen.
+    verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerHasNoPreference()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifMiddleUses_DisplayP3_Then_DisplayP3_Chosen) {
+    // If only the middle layer has a preference, that that is what is chosen.
+    verify().ifTopLayerHasNoPreference()
+            .andIfMiddleLayerIs(ui::Dataspace::DISPLAY_P3)
+            .andIfBottomLayerHasNoPreference()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifBottomUses_DisplayP3_Then_DisplayP3_Chosen) {
+    // If only the middle layer has a preference, that that is what is chosen.
+    verify().ifTopLayerHasNoPreference()
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifTopUses_DisplayBT2020_AndBottomUses_DisplayP3_Then_DisplayBT2020_Chosen) {
+    // If multiple layers have a preference, the topmost value is what is used.
+    verify().ifTopLayerIs(ui::Dataspace::DISPLAY_BT2020)
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+       ifTopUses_DisplayP3_AndBottomUses_V0_SRGB_Then_DisplayP3_Chosen) {
+    // If multiple layers have a preference, the topmost value is what is used.
+    verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+            .andIfMiddleLayerHasNoPreference()
+            .andIfBottomLayerIs(ui::Dataspace::DISPLAY_BT2020)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+struct OutputUpdateColorProfileTest_ForceOutputColorOverrides
+      : public OutputUpdateColorProfileTest {
+    // If CompositionRefreshArgs::forceOutputColorMode is set to some specific
+    // values, it overrides the layer dataspace choice.
+
+    OutputUpdateColorProfileTest_ForceOutputColorOverrides() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+        mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+    }
+
+    struct IfForceOutputColorModeState
+          : public CallOrderStateMachineHelper<TestType, IfForceOutputColorModeState> {
+        [[nodiscard]] auto ifForceOutputColorMode(ui::ColorMode colorMode) {
+            getInstance()->mRefreshArgs.forceOutputColorMode = colorMode;
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+        [[nodiscard]] auto ifNoOverride() { return ifForceOutputColorMode(ui::ColorMode::NATIVE); }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(dataspace, _, _, _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfForceOutputColorModeState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, NoOverride_DoesNotOverride) {
+    // By default the layer state is used to set the preferred dataspace
+    verify().ifNoOverride()
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, SRGB_Override_USES_V0_SRGB) {
+    // Setting ui::ColorMode::SRGB overrides it with ui::Dataspace::V0_SRGB
+    verify().ifForceOutputColorMode(ui::ColorMode::SRGB)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, DisplayP3_Override_Uses_DisplayP3) {
+    // Setting ui::ColorMode::DISPLAY_P3 overrides it with ui::Dataspace::DISPLAY_P3
+    verify().ifForceOutputColorMode(ui::ColorMode::DISPLAY_P3)
+            .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+            .execute();
+}
+
+// HDR output requires all layers to be compatible with the chosen HDR
+// dataspace, along with there being proper support.
+struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest {
+    OutputUpdateColorProfileTest_Hdr() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+    }
+
+    static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+    static constexpr ui::Dataspace BT2020_PQ = ui::Dataspace::BT2020_PQ;
+    static constexpr ui::Dataspace BT2020_HLG = ui::Dataspace::BT2020_HLG;
+    static constexpr ui::Dataspace DISPLAY_P3 = ui::Dataspace::DISPLAY_P3;
+
+    struct IfTopLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+        [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+            return nextState<AndTopLayerCompositionTypeState>();
+        }
+        [[nodiscard]] auto ifTopLayerIsNotHdr() { return ifTopLayerIs(kNonHdrDataspace); }
+    };
+
+    struct AndTopLayerCompositionTypeState
+          : public CallOrderStateMachineHelper<TestType, AndTopLayerCompositionTypeState> {
+        [[nodiscard]] auto andTopLayerIsREComposed(bool renderEngineComposed) {
+            getInstance()->mLayer2.mLayerFEState.forceClientComposition = renderEngineComposed;
+            return nextState<AndIfBottomLayerDataspaceState>();
+        }
+    };
+
+    struct AndIfBottomLayerDataspaceState
+          : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+        [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+            return nextState<AndBottomLayerCompositionTypeState>();
+        }
+        [[nodiscard]] auto andIfBottomLayerIsNotHdr() {
+            return andIfBottomLayerIs(kNonHdrDataspace);
+        }
+    };
+
+    struct AndBottomLayerCompositionTypeState
+          : public CallOrderStateMachineHelper<TestType, AndBottomLayerCompositionTypeState> {
+        [[nodiscard]] auto andBottomLayerIsREComposed(bool renderEngineComposed) {
+            getInstance()->mLayer1.mLayerFEState.forceClientComposition = renderEngineComposed;
+            return nextState<AndIfHasLegacySupportState>();
+        }
+    };
+
+    struct AndIfHasLegacySupportState
+          : public CallOrderStateMachineHelper<TestType, AndIfHasLegacySupportState> {
+        [[nodiscard]] auto andIfLegacySupportFor(ui::Dataspace dataspace, bool legacySupport) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasLegacyHdrSupport(dataspace))
+                    .WillOnce(Return(legacySupport));
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(dataspace, _, _, _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_Uses_PQ) {
+    // If all layers use BT2020_PQ, and there are no other special conditions,
+    // BT2020_PQ is used.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_PQ is not used if there is only legacy support for it.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_RE_Uses_PQ) {
+    // BT2020_PQ is still used if the bottom layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_PQ_HW_Uses_DisplayP3) {
+    // BT2020_PQ is not used if the top layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_Uses_PQ) {
+    // If there is mixed HLG/PQ use, and the topmost layer is PQ, then PQ is used if there
+    // are no other special conditions.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_PQ is not used if there is only legacy support for it.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_RE_Uses_PQ) {
+    // BT2020_PQ is used if the bottom HLG layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_HLG_HW_Uses_DisplayP3) {
+    // BT2020_PQ is not used if the top PQ layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_Uses_PQ) {
+    // If there is mixed HLG/PQ use, and the topmost layer is HLG, then PQ is
+    // used if there are no other special conditions.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_PQ is not used if there is only legacy support for it.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_RE_Uses_DisplayP3) {
+    // BT2020_PQ is not used if the bottom PQ layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_PQ_HW_Uses_PQ) {
+    // BT2020_PQ is still used if the top HLG layer is RenderEngine composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_PQ)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_Uses_HLG) {
+    // If all layers use HLG then HLG is used if there are no other special
+    // conditions.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+    // BT2020_HLG is not used if there is legacy support for it.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_HLG, true)
+            .thenExpectBestColorModeCallUses(DISPLAY_P3)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_RE_Uses_HLG) {
+    // BT2020_HLG is used even if the bottom layer is client composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_HLG_HW_Uses_HLG) {
+    // BT2020_HLG is used even if the top layer is client composed.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(true)
+            .andIfBottomLayerIs(BT2020_HLG)
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_NonHdr_HW_Uses_PQ) {
+    // Even if there are non-HDR layers present, BT2020_PQ can still be used.
+    verify().ifTopLayerIs(BT2020_PQ)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIsNotHdr()
+            .andBottomLayerIsREComposed(false)
+            .andIfLegacySupportFor(BT2020_PQ, false)
+            .thenExpectBestColorModeCallUses(BT2020_PQ)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_NonHdr_RE_Uses_HLG) {
+    // If all layers use HLG then HLG is used if there are no other special
+    // conditions.
+    verify().ifTopLayerIs(BT2020_HLG)
+            .andTopLayerIsREComposed(false)
+            .andIfBottomLayerIsNotHdr()
+            .andBottomLayerIsREComposed(true)
+            .andIfLegacySupportFor(BT2020_HLG, false)
+            .thenExpectBestColorModeCallUses(BT2020_HLG)
+            .execute();
+}
+
+struct OutputUpdateColorProfile_AffectsChosenRenderIntentTest
+      : public OutputUpdateColorProfileTest {
+    // The various values for CompositionRefreshArgs::outputColorSetting affect
+    // the chosen renderIntent, along with whether the preferred dataspace is an
+    // HDR dataspace or not.
+
+    OutputUpdateColorProfile_AffectsChosenRenderIntentTest() {
+        mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+        mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+        mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
+        EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+        EXPECT_CALL(*mDisplayColorProfile, hasLegacyHdrSupport(ui::Dataspace::BT2020_PQ))
+                .WillRepeatedly(Return(false));
+    }
+
+    // The tests here involve enough state and GMock setup that using a mini-DSL
+    // makes the tests much more readable, and allows the test to focus more on
+    // the intent than on some of the details.
+
+    static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+    static constexpr ui::Dataspace kHdrDataspace = ui::Dataspace::BT2020_PQ;
+
+    struct IfDataspaceChosenState
+          : public CallOrderStateMachineHelper<TestType, IfDataspaceChosenState> {
+        [[nodiscard]] auto ifDataspaceChosenIs(ui::Dataspace dataspace) {
+            getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+            return nextState<AndOutputColorSettingState>();
+        }
+        [[nodiscard]] auto ifDataspaceChosenIsNonHdr() {
+            return ifDataspaceChosenIs(kNonHdrDataspace);
+        }
+        [[nodiscard]] auto ifDataspaceChosenIsHdr() { return ifDataspaceChosenIs(kHdrDataspace); }
+    };
+
+    struct AndOutputColorSettingState
+          : public CallOrderStateMachineHelper<TestType, AndOutputColorSettingState> {
+        [[nodiscard]] auto andOutputColorSettingIs(OutputColorSetting setting) {
+            getInstance()->mRefreshArgs.outputColorSetting = setting;
+            return nextState<ThenExpectBestColorModeCallUsesState>();
+        }
+    };
+
+    struct ThenExpectBestColorModeCallUsesState
+          : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+        [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::RenderIntent intent) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+                        getBestColorMode(getInstance()->mLayer1.mLayerFEState.dataspace, intent, _,
+                                         _, _));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Tests call one of these two helper member functions to start using the
+    // mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfDataspaceChosenState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+       Managed_NonHdr_Prefers_Colorimetric) {
+    verify().ifDataspaceChosenIsNonHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kManaged)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::COLORIMETRIC)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+       Managed_Hdr_Prefers_ToneMapColorimetric) {
+    verify().ifDataspaceChosenIsHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kManaged)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_COLORIMETRIC)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Enhanced_NonHdr_Prefers_Enhance) {
+    verify().ifDataspaceChosenIsNonHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::ENHANCE)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+       Enhanced_Hdr_Prefers_ToneMapEnhance) {
+    verify().ifDataspaceChosenIsHdr()
+            .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+            .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_ENHANCE)
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_NonHdr_Prefers_Vendor) {
+    verify().ifDataspaceChosenIsNonHdr()
+            .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+            .thenExpectBestColorModeCallUses(
+                    static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+            .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_Hdr_Prefers_Vendor) {
+    verify().ifDataspaceChosenIsHdr()
+            .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+            .thenExpectBestColorModeCallUses(
+                    static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+            .execute();
+}
+
+/*
+ * Output::beginFrame()
+ */
+
+struct OutputBeginFrameTest : public ::testing::Test {
+    using TestType = OutputBeginFrameTest;
+
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+    };
+
+    OutputBeginFrameTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    struct IfGetDirtyRegionExpectationState
+          : public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> {
+        [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) {
+            EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false))
+                    .WillOnce(Return(dirtyRegion));
+            return nextState<AndIfGetOutputLayerCountExpectationState>();
+        }
+    };
+
+    struct AndIfGetOutputLayerCountExpectationState
+          : public CallOrderStateMachineHelper<TestType, AndIfGetOutputLayerCountExpectationState> {
+        [[nodiscard]] auto andIfGetOutputLayerCountReturns(size_t layerCount) {
+            EXPECT_CALL(getInstance()->mOutput, getOutputLayerCount()).WillOnce(Return(layerCount));
+            return nextState<AndIfLastCompositionHadVisibleLayersState>();
+        }
+    };
+
+    struct AndIfLastCompositionHadVisibleLayersState
+          : public CallOrderStateMachineHelper<TestType,
+                                               AndIfLastCompositionHadVisibleLayersState> {
+        [[nodiscard]] auto andIfLastCompositionHadVisibleLayersIs(bool hadOutputLayers) {
+            getInstance()->mOutput.mState.lastCompositionHadVisibleLayers = hadOutputLayers;
+            return nextState<ThenExpectRenderSurfaceBeginFrameCallState>();
+        }
+    };
+
+    struct ThenExpectRenderSurfaceBeginFrameCallState
+          : public CallOrderStateMachineHelper<TestType,
+                                               ThenExpectRenderSurfaceBeginFrameCallState> {
+        [[nodiscard]] auto thenExpectRenderSurfaceBeginFrameCall(bool mustRecompose) {
+            EXPECT_CALL(*getInstance()->mRenderSurface, beginFrame(mustRecompose));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+        [[nodiscard]] auto execute() {
+            getInstance()->mOutput.beginFrame();
+            return nextState<CheckPostconditionHadVisibleLayersState>();
+        }
+    };
+
+    struct CheckPostconditionHadVisibleLayersState
+          : public CallOrderStateMachineHelper<TestType, CheckPostconditionHadVisibleLayersState> {
+        void checkPostconditionHadVisibleLayers(bool expected) {
+            EXPECT_EQ(expected, getInstance()->mOutput.mState.lastCompositionHadVisibleLayers);
+        }
+    };
+
+    // Tests call one of these two helper member functions to start using the
+    // mini-DSL defined above.
+    [[nodiscard]] auto verify() { return IfGetDirtyRegionExpectationState::make(this); }
+
+    static const Region kEmptyRegion;
+    static const Region kNotEmptyRegion;
+
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+};
+
+const Region OutputBeginFrameTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputBeginFrameTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(true)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(true)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(true)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(true)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(1u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersNotHadLayersLastFrame) {
+    verify().ifGetDirtyRegionReturns(kEmptyRegion)
+            .andIfGetOutputLayerCountReturns(0u)
+            .andIfLastCompositionHadVisibleLayersIs(false)
+            .thenExpectRenderSurfaceBeginFrameCall(false)
+            .execute()
+            .checkPostconditionHadVisibleLayers(false);
+}
+
+/*
+ * Output::devOptRepaintFlash()
+ */
+
+struct OutputDevOptRepaintFlashTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+        MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+        MOCK_METHOD0(postFramebuffer, void());
+        MOCK_METHOD0(prepareFrame, void());
+    };
+
+    OutputDevOptRepaintFlashTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    static const Region kEmptyRegion;
+    static const Region kNotEmptyRegion;
+
+    StrictMock<OutputPartialMock> mOutput;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+const Region OutputDevOptRepaintFlashTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputDevOptRepaintFlashTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = {};
+    mRefreshArgs.repaintEverything = true;
+    mOutput.mState.isEnabled = true;
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+    mRefreshArgs.repaintEverything = true;
+    mOutput.mState.isEnabled = false;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, postFramebuffer());
+    EXPECT_CALL(mOutput, prepareFrame());
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+    mRefreshArgs.repaintEverything = true;
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion));
+    EXPECT_CALL(mOutput, postFramebuffer());
+    EXPECT_CALL(mOutput, prepareFrame());
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) {
+    mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+    mRefreshArgs.repaintEverything = false;
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion)));
+    EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+    EXPECT_CALL(mOutput, postFramebuffer());
+    EXPECT_CALL(mOutput, prepareFrame());
+
+    mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+/*
+ * Output::finishFrame()
+ */
+
+struct OutputFinishFrameTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+        MOCK_METHOD0(postFramebuffer, void());
+    };
+
+    OutputFinishFrameTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    StrictMock<OutputPartialMock> mOutput;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
+    mOutput.mState.isEnabled = false;
+
+    mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)));
+
+    mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
+    mOutput.mState.isEnabled = true;
+
+    InSequence seq;
+    EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)))
+            .WillOnce(Return(ByMove(base::unique_fd())));
+    EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+
+    mOutput.finishFrame(mRefreshArgs);
+}
+
+/*
+ * Output::postFramebuffer()
+ */
+
+struct OutputPostFramebufferTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+            EXPECT_CALL(outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
+        }
+
+        StrictMock<mock::OutputLayer> outputLayer;
+        StrictMock<mock::LayerFE> layerFE;
+        StrictMock<HWC2::mock::Layer> hwc2Layer;
+    };
+
+    OutputPostFramebufferTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer1.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+                .WillRepeatedly(Return(&mLayer2.outputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2u))
+                .WillRepeatedly(Return(&mLayer3.outputLayer));
+    }
+
+    StrictMock<OutputPartialMock> mOutput;
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+
+    Layer mLayer1;
+    Layer mLayer2;
+    Layer mLayer3;
+};
+
+TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
+    mOutput.mState.isEnabled = false;
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
+    mOutput.mState.isEnabled = true;
+
+    compositionengine::Output::FrameFences frameFences;
+
+    // This should happen even if there are no output layers.
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+    // For this test in particular we want to make sure the call expectations
+    // setup below are satisfied in the specific order.
+    InSequence seq;
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
+    // Simulate getting release fences from each layer, and ensure they are passed to the
+    // front-end layer interface for each layer correctly.
+
+    mOutput.mState.isEnabled = true;
+
+    // Create three unique fence instances
+    sp<Fence> layer1Fence = new Fence();
+    sp<Fence> layer2Fence = new Fence();
+    sp<Fence> layer3Fence = new Fence();
+
+    Output::FrameFences frameFences;
+    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    // Compare the pointers values of each fence to make sure the correct ones
+    // are passed. This happens to work with the current implementation, but
+    // would not survive certain calls like Fence::merge() which would return a
+    // new instance.
+    EXPECT_CALL(mLayer1.layerFE,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
+    EXPECT_CALL(mLayer2.layerFE,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
+    EXPECT_CALL(mLayer3.layerFE,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
+    mOutput.mState.isEnabled = true;
+    mOutput.mState.usesClientComposition = true;
+
+    sp<Fence> clientTargetAcquireFence = new Fence();
+    sp<Fence> layer1Fence = new Fence();
+    sp<Fence> layer2Fence = new Fence();
+    sp<Fence> layer3Fence = new Fence();
+    Output::FrameFences frameFences;
+    frameFences.clientTargetAcquireFence = clientTargetAcquireFence;
+    frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+    frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+    frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    // Fence::merge is called, and since none of the fences are actually valid,
+    // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
+    // This is the best we can do without creating a real kernel fence object.
+    EXPECT_CALL(mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+    EXPECT_CALL(mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+
+    mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
+    mOutput.mState.isEnabled = true;
+    mOutput.mState.usesClientComposition = true;
+
+    // This should happen even if there are no (current) output layers.
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+    // Load up the released layers with some mock instances
+    sp<StrictMock<mock::LayerFE>> releasedLayer1{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> releasedLayer2{new StrictMock<mock::LayerFE>()};
+    sp<StrictMock<mock::LayerFE>> releasedLayer3{new StrictMock<mock::LayerFE>()};
+    Output::ReleasedLayers layers;
+    layers.push_back(releasedLayer1);
+    layers.push_back(releasedLayer2);
+    layers.push_back(releasedLayer3);
+    mOutput.setReleasedLayers(std::move(layers));
+
+    // Set up a fake present fence
+    sp<Fence> presentFence = new Fence();
+    Output::FrameFences frameFences;
+    frameFences.presentFence = presentFence;
+
+    EXPECT_CALL(*mRenderSurface, flip());
+    EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+    EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+    // Each released layer should be given the presentFence.
+    EXPECT_CALL(*releasedLayer1,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+    EXPECT_CALL(*releasedLayer2,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+    EXPECT_CALL(*releasedLayer3,
+                onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+
+    mOutput.postFramebuffer();
+
+    // After the call the list of released layers should have been cleared.
+    EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
+}
+
+/*
+ * Output::composeSurfaces()
+ */
+
+struct OutputComposeSurfacesTest : public testing::Test {
+    using TestType = OutputComposeSurfacesTest;
+
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // Sets up the helper functions called by the function under test to use
+        // mock implementations.
+        MOCK_CONST_METHOD0(getSkipColorTransform, bool());
+        MOCK_METHOD3(generateClientCompositionRequests,
+                     std::vector<LayerFE::LayerSettings>(bool, Region&, ui::Dataspace));
+        MOCK_METHOD2(appendRegionFlashRequests,
+                     void(const Region&, std::vector<LayerFE::LayerSettings>&));
+        MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+    };
+
+    OutputComposeSurfacesTest() {
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+        mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE);
+
+        mOutput.mState.frame = kDefaultOutputFrame;
+        mOutput.mState.viewport = kDefaultOutputViewport;
+        mOutput.mState.sourceClip = kDefaultOutputSourceClip;
+        mOutput.mState.destinationClip = kDefaultOutputDestinationClip;
+        mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
+        mOutput.mState.orientation = kDefaultOutputOrientation;
+        mOutput.mState.dataspace = kDefaultOutputDataspace;
+        mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
+        mOutput.mState.isSecure = false;
+        mOutput.mState.needsFiltering = false;
+        mOutput.mState.usesClientComposition = true;
+        mOutput.mState.usesDeviceComposition = false;
+        mOutput.mState.reusedClientComposition = false;
+
+        EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
+        EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+        EXPECT_CALL(mCompositionEngine, getTimeStats())
+                .WillRepeatedly(ReturnRef(*mTimeStats.get()));
+        EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities())
+                .WillRepeatedly(ReturnRef(kHdrCapabilities));
+    }
+
+    struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+        auto execute() {
+            getInstance()->mReadyFence = getInstance()->mOutput.composeSurfaces(kDebugRegion);
+            return nextState<FenceCheckState>();
+        }
+    };
+
+    struct FenceCheckState : public CallOrderStateMachineHelper<TestType, FenceCheckState> {
+        void expectNoFenceWasReturned() { EXPECT_FALSE(getInstance()->mReadyFence); }
+
+        void expectAFenceWasReturned() { EXPECT_TRUE(getInstance()->mReadyFence); }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return ExecuteState::make(this); }
+
+    static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+    static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
+    static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
+    static constexpr float kDefaultMaxLuminance = 0.9f;
+    static constexpr float kDefaultAvgLuminance = 0.7f;
+    static constexpr float kDefaultMinLuminance = 0.1f;
+
+    static const Rect kDefaultOutputFrame;
+    static const Rect kDefaultOutputViewport;
+    static const Rect kDefaultOutputSourceClip;
+    static const Rect kDefaultOutputDestinationClip;
+    static const mat4 kDefaultColorTransformMat;
+
+    static const Region kDebugRegion;
+    static const HdrCapabilities kHdrCapabilities;
+
+    StrictMock<mock::CompositionEngine> mCompositionEngine;
+    StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
+    // TODO: make this is a proper mock.
+    std::shared_ptr<TimeStats> mTimeStats = std::make_shared<android::impl::TimeStats>();
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+    sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+
+    std::optional<base::unique_fd> mReadyFence;
+};
+
+const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
+const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
+const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012};
+const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
+const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
+const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
+const HdrCapabilities OutputComposeSurfacesTest::
+        kHdrCapabilities{{},
+                         OutputComposeSurfacesTest::kDefaultMaxLuminance,
+                         OutputComposeSurfacesTest::kDefaultAvgLuminance,
+                         OutputComposeSurfacesTest::kDefaultMinLuminance};
+
+TEST_F(OutputComposeSurfacesTest, doesNothingButSignalNoExpensiveRenderingIfNoClientComposition) {
+    mOutput.mState.usesClientComposition = false;
+
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFails) {
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
+
+    verify().execute().expectNoFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) {
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(
+                    Invoke([&](const Region&,
+                               std::vector<LayerFE::LayerSettings>& clientCompositionLayers) {
+                        clientCompositionLayers.emplace_back(r2);
+                    }));
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest, renderDuplicateClientCompositionRequestsWithoutCache) {
+    mOutput.cacheClientCompositionRequests(0);
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .Times(2)
+            .WillOnce(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+}
+
+TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) {
+    mOutput.cacheClientCompositionRequests(3);
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    // We do not expect another call to draw layers.
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_TRUE(mOutput.mState.reusedClientComposition);
+}
+
+TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    sp<GraphicBuffer> otherOutputBuffer = new GraphicBuffer();
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
+            .WillOnce(Return(mOutputBuffer))
+            .WillOnce(Return(otherOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillRepeatedly(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+}
+
+TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) {
+    LayerFE::LayerSettings r1;
+    LayerFE::LayerSettings r2;
+    LayerFE::LayerSettings r3;
+
+    r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+    r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+    r3.geometry.boundaries = FloatRect{5, 6, 7, 9};
+
+    EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+    EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2}))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3}));
+    EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+            .WillRepeatedly(Return());
+
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r2)), _, true, _, _))
+            .WillOnce(Return(NO_ERROR));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(Pointee(r1), Pointee(r3)), _, true, _, _))
+            .WillOnce(Return(NO_ERROR));
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+    verify().execute().expectAFenceWasReturned();
+    EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+}
+
+struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
+    OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
+        EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+                .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
+        EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+                .WillRepeatedly(Return());
+        EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    }
+
+    struct MixedCompositionState
+          : public CallOrderStateMachineHelper<TestType, MixedCompositionState> {
+        auto ifMixedCompositionIs(bool used) {
+            getInstance()->mOutput.mState.usesDeviceComposition = used;
+            return nextState<OutputUsesHdrState>();
+        }
+    };
+
+    struct OutputUsesHdrState : public CallOrderStateMachineHelper<TestType, OutputUsesHdrState> {
+        auto andIfUsesHdr(bool used) {
+            EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasWideColorGamut())
+                    .WillOnce(Return(used));
+            return nextState<SkipColorTransformState>();
+        }
+    };
+
+    struct SkipColorTransformState
+          : public CallOrderStateMachineHelper<TestType, SkipColorTransformState> {
+        auto andIfSkipColorTransform(bool skip) {
+            // May be called zero or one times.
+            EXPECT_CALL(getInstance()->mOutput, getSkipColorTransform())
+                    .WillRepeatedly(Return(skip));
+            return nextState<ExpectDisplaySettingsState>();
+        }
+    };
+
+    struct ExpectDisplaySettingsState
+          : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
+        auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
+            EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, true, _, _))
+                    .WillOnce(Return(NO_ERROR));
+            return nextState<ExecuteState>();
+        }
+    };
+
+    // Call this member function to start using the mini-DSL defined above.
+    [[nodiscard]] auto verify() { return MixedCompositionState::make(this); }
+};
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) {
+    verify().ifMixedCompositionIs(true)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            mat4(), Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) {
+    verify().ifMixedCompositionIs(true)
+            .andIfUsesHdr(false)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            mat4(), Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(false)
+            .andIfSkipColorTransform(false)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            kDefaultColorTransformMat, Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+       usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) {
+    verify().ifMixedCompositionIs(false)
+            .andIfUsesHdr(true)
+            .andIfSkipColorTransform(true)
+            .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip,
+                                            mat4(), kDefaultMaxLuminance, kDefaultOutputDataspace,
+                                            mat4(), Region::INVALID_REGION,
+                                            kDefaultOutputOrientation})
+            .execute()
+            .expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::LayerFE> mLayerFE;
+        LayerFECompositionState mLayerFEState;
+    };
+
+    OutputComposeSurfacesTest_HandlesProtectedContent() {
+        mLayer1.mLayerFEState.hasProtectedContent = false;
+        mLayer2.mLayerFEState.hasProtectedContent = false;
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+                .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+        EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+                .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+
+        EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+
+        EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+
+        EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _))
+                .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{}));
+        EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+                .WillRepeatedly(Return());
+        EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+        EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _))
+                .WillRepeatedly(Return(NO_ERROR));
+    }
+
+    Layer mLayer1;
+    Layer mLayer2;
+};
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) {
+    mOutput.mState.isSecure = false;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = false;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(false));
+    EXPECT_CALL(*mRenderSurface, setProtected(false));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+    // For this test, we also check the call order of key functions.
+    InSequence seq;
+
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, setProtected(true));
+    // Must happen after setting the protected content state.
+    EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, setProtected(true));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
+    mOutput.mState.isSecure = true;
+    mLayer2.mLayerFEState.hasProtectedContent = true;
+    EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+    EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+    EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+    EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
+    OutputComposeSurfacesTest_SetsExpensiveRendering() {
+        EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+        EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+        EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+        EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+                .WillRepeatedly(Return());
+        EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+    }
+};
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
+    mOutput.mState.dataspace = kExpensiveOutputDataspace;
+
+    EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>{}));
+
+    // For this test, we also check the call order of key functions.
+    InSequence seq;
+
+    EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
+    EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+
+    mOutput.composeSurfaces(kDebugRegion);
+}
+
+/*
+ * Output::generateClientCompositionRequests()
+ */
+
+struct GenerateClientCompositionRequestsTest : public testing::Test {
+    struct OutputPartialMock : public OutputPartialMockBase {
+        // compositionengine::Output overrides
+        std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
+                bool supportsProtectedContent, Region& clearRegion,
+                ui::Dataspace dataspace) override {
+            return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
+                                                                   clearRegion, dataspace);
+        }
+    };
+
+    struct Layer {
+        Layer() {
+            EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+            EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+            EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+            EXPECT_CALL(mLayerFE, getCompositionState()).WillRepeatedly(Return(&mLayerFEState));
+        }
+
+        StrictMock<mock::OutputLayer> mOutputLayer;
+        StrictMock<mock::LayerFE> mLayerFE;
+        LayerFECompositionState mLayerFEState;
+        impl::OutputLayerCompositionState mOutputLayerState;
+        LayerFE::LayerSettings mLayerSettings;
+    };
+
+    GenerateClientCompositionRequestsTest() {
+        mOutput.mState.needsFiltering = false;
+
+        mOutput.setDisplayColorProfileForTest(
+                std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+        mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+    }
+
+    mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+    mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+    StrictMock<OutputPartialMock> mOutput;
+};
+
+struct GenerateClientCompositionRequestsTest_ThreeLayers
+      : public GenerateClientCompositionRequestsTest {
+    GenerateClientCompositionRequestsTest_ThreeLayers() {
+        mOutput.mState.frame = kDisplayFrame;
+        mOutput.mState.viewport = kDisplayViewport;
+        mOutput.mState.sourceClip = kDisplaySourceClip;
+        mOutput.mState.destinationClip = kDisplayDestinationClip;
+        mOutput.mState.transform = ui::Transform{kDisplayOrientation};
+        mOutput.mState.orientation = kDisplayOrientation;
+        mOutput.mState.needsFiltering = false;
+        mOutput.mState.isSecure = false;
+
+        for (size_t i = 0; i < mLayers.size(); i++) {
+            mLayers[i].mOutputLayerState.clearClientTarget = false;
+            mLayers[i].mOutputLayerState.visibleRegion = Region(kDisplayFrame);
+            mLayers[i].mLayerFEState.isOpaque = true;
+            mLayers[i].mLayerSettings.geometry.boundaries =
+                    FloatRect{static_cast<float>(i + 1), 0.f, 0.f, 0.f};
+            mLayers[i].mLayerSettings.source.solidColor = {1.0f, 1.0f, 1.0f};
+            mLayers[i].mLayerSettings.alpha = 1.0f;
+            mLayers[i].mLayerSettings.disableBlending = false;
+
+            EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(i))
+                    .WillRepeatedly(Return(&mLayers[i].mOutputLayer));
+            EXPECT_CALL(mLayers[i].mOutputLayer, requiresClientComposition())
+                    .WillRepeatedly(Return(true));
+            EXPECT_CALL(mLayers[i].mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+        }
+
+        EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
+    }
+
+    static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+    static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
+
+    static const Rect kDisplayFrame;
+    static const Rect kDisplayViewport;
+    static const Rect kDisplaySourceClip;
+    static const Rect kDisplayDestinationClip;
+
+    std::array<Layer, 3> mLayers;
+};
+
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103,
+                                                                                      203);
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) {
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    EXPECT_EQ(0u, requests.size());
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) {
+    mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 10, 10));
+    mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
+    mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    EXPECT_EQ(0u, requests.size());
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
+    LayerFE::LayerSettings mShadowSettings;
+    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[1].mLayerSettings})));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
+                    {mShadowSettings, mLayers[2].mLayerSettings})));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(3u, requests.size());
+    EXPECT_EQ(mLayers[1].mLayerSettings, requests[0]);
+    EXPECT_EQ(mShadowSettings, requests[1]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[2]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+
+    // Check that a timestamp was set for the layers that generated requests
+    EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
+    EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
+    EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       onlyClientComposesClientComposedLayersIfNoClearingNeeded) {
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    mLayers[0].mOutputLayerState.clearClientTarget = false;
+    mLayers[1].mOutputLayerState.clearClientTarget = false;
+    mLayers[2].mOutputLayerState.clearClientTarget = false;
+
+    mLayers[0].mLayerFEState.isOpaque = true;
+    mLayers[1].mLayerFEState.isOpaque = true;
+    mLayers[2].mLayerFEState.isOpaque = true;
+
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       onlyClientComposesClientComposedLayersIfOthersAreNotOpaque) {
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    mLayers[0].mOutputLayerState.clearClientTarget = true;
+    mLayers[1].mOutputLayerState.clearClientTarget = true;
+    mLayers[2].mOutputLayerState.clearClientTarget = true;
+
+    mLayers[0].mLayerFEState.isOpaque = false;
+    mLayers[1].mLayerFEState.isOpaque = false;
+    mLayers[2].mLayerFEState.isOpaque = false;
+
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(_))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[0]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) {
+    // If client composition is performed with some layers set to use device
+    // composition, device layers after the first layer (device or client) will
+    // clear the frame buffer if they are opaque and if that layer has a flag
+    // set to do so. The first layer is skipped as the frame buffer is already
+    // expected to be clear.
+
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+    mLayers[0].mOutputLayerState.clearClientTarget = true;
+    mLayers[1].mOutputLayerState.clearClientTarget = true;
+    mLayers[2].mOutputLayerState.clearClientTarget = true;
+
+    mLayers[0].mLayerFEState.isOpaque = true;
+    mLayers[1].mLayerFEState.isOpaque = true;
+    mLayers[2].mLayerFEState.isOpaque = true;
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    Region dummyRegion;
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false,       /* identity transform */
+            false,       /* needs filtering */
+            false,       /* secure */
+            false,       /* supports protected content */
+            dummyRegion, /* clear region */
+            kDisplayViewport,
+            kDisplayDataspace,
+            false /* realContentIsVisible */,
+            true /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
+    mBlackoutSettings.source.buffer.buffer = nullptr;
+    mBlackoutSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+    mBlackoutSettings.alpha = 0.f;
+    mBlackoutSettings.disableBlending = true;
+
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mBlackoutSettings})));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mLayers[2].mLayerSettings})));
+
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
+
+    // The second layer is expected to be rendered as alpha=0 black with no blending
+    EXPECT_EQ(mBlackoutSettings, requests[0]);
+
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
+
+    EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       clippedVisibleRegionUsedToGenerateRequest) {
+    mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 20, 20));
+    mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30));
+    mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(Rect(10, 10, 20, 20)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(Rect(0, 0, 30, 30)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(Rect(0, 0, 40, 201)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       perLayerNeedsFilteringUsedToGenerateRequests) {
+    mOutput.mState.needsFiltering = false;
+    EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       wholeOutputNeedsFilteringUsedToGenerateRequests) {
+    mOutput.mState.needsFiltering = true;
+    EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            true,  /* needs filtering */
+            false, /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       wholeOutputSecurityUsedToGenerateRequests) {
+    mOutput.mState.isSecure = true;
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(
+            mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                      accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       protectedContentSupportUsedToGenerateRequests) {
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+            Region(kDisplayFrame),
+            false, /* identity transform */
+            false, /* needs filtering */
+            false, /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[1].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer1TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2TargetSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>()));
+
+    static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
+                                                                accumClearRegion,
+                                                                kDisplayDataspace));
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) {
+    InjectedLayer layer1;
+    InjectedLayer layer2;
+    InjectedLayer layer3;
+
+    // Layer requesting blur, or below, should request client composition.
+    EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true));
+    EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false));
+    EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false));
+    EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false));
+
+    layer2.layerFEState.backgroundBlurRadius = 10;
+
+    injectOutputLayer(layer1);
+    injectOutputLayer(layer2);
+    injectOutputLayer(layer3);
+
+    mOutput->editState().isEnabled = true;
+
+    CompositionRefreshArgs args;
+    args.updatingGeometryThisFrame = false;
+    args.devOptForceClientComposition = false;
+    mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
+    // In split-screen landscape mode, the screen is rotated 90 degrees, with
+    // one layer on the left covering the left side of the output, and one layer
+    // on the right covering that side of the output.
+
+    const Rect kPortraitFrame(0, 0, 1000, 2000);
+    const Rect kPortraitViewport(0, 0, 2000, 1000);
+    const Rect kPortraitSourceClip(0, 0, 1000, 2000);
+    const Rect kPortraitDestinationClip(0, 0, 1000, 2000);
+    const uint32_t kPortraitOrientation = TR_ROT_90;
+    constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
+
+    mOutput.mState.frame = kPortraitFrame;
+    mOutput.mState.viewport = kPortraitViewport;
+    mOutput.mState.sourceClip = kPortraitSourceClip;
+    mOutput.mState.destinationClip = kPortraitDestinationClip;
+    mOutput.mState.transform = ui::Transform{kPortraitOrientation};
+    mOutput.mState.orientation = kPortraitOrientation;
+    mOutput.mState.needsFiltering = false;
+    mOutput.mState.isSecure = true;
+
+    Layer leftLayer;
+    Layer rightLayer;
+
+    leftLayer.mOutputLayerState.clearClientTarget = false;
+    leftLayer.mOutputLayerState.visibleRegion = Region(Rect(0, 0, 1000, 1000));
+    leftLayer.mLayerFEState.isOpaque = true;
+    leftLayer.mLayerSettings.source.solidColor = {1.f, 0.f, 0.f};
+
+    rightLayer.mOutputLayerState.clearClientTarget = false;
+    rightLayer.mOutputLayerState.visibleRegion = Region(Rect(1000, 0, 2000, 1000));
+    rightLayer.mLayerFEState.isOpaque = true;
+    rightLayer.mLayerSettings.source.solidColor = {0.f, 1.f, 0.f};
+
+    EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+            .WillRepeatedly(Return(&leftLayer.mOutputLayer));
+    EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+            .WillRepeatedly(Return(&rightLayer.mOutputLayer));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
+            Region(Rect(0, 0, 1000, 1000)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kPortraitViewport,
+            kOutputDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+    EXPECT_CALL(leftLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(leftLayerSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({leftLayer.mLayerSettings})));
+
+    compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
+            Region(Rect(1000, 0, 2000, 1000)),
+            false, /* identity transform */
+            false, /* needs filtering */
+            true,  /* secure */
+            true,  /* supports protected content */
+            accumClearRegion,
+            kPortraitViewport,
+            kOutputDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+    EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+    EXPECT_CALL(rightLayer.mLayerFE, prepareClientCompositionList(Eq(ByRef(rightLayerSettings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({rightLayer.mLayerSettings})));
+
+    constexpr bool supportsProtectedContent = true;
+    auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
+                                                              accumClearRegion, kOutputDataspace);
+    ASSERT_EQ(2u, requests.size());
+    EXPECT_EQ(leftLayer.mLayerSettings, requests[0]);
+    EXPECT_EQ(rightLayer.mLayerSettings, requests[1]);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       shadowRegionOnlyVisibleSkipsContentComposition) {
+    const Rect kContentWithShadow(40, 40, 70, 90);
+    const Rect kContent(50, 50, 60, 80);
+    const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+    const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80));
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
+            Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
+            false,                                                    /* identity transform */
+            false,                                                    /* needs filtering */
+            false,                                                    /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            false /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    LayerFE::LayerSettings mShadowSettings;
+    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    mLayers[2].mOutputLayerState.visibleRegion = kPartialShadowRegion;
+    mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>({mShadowSettings})));
+
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(1u, requests.size());
+
+    EXPECT_EQ(mShadowSettings, requests[0]);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+       shadowRegionWithContentVisibleRequestsContentAndShadowComposition) {
+    const Rect kContentWithShadow(40, 40, 70, 90);
+    const Rect kContent(50, 50, 60, 80);
+    const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+    const Region kPartialContentWithPartialShadowRegion =
+            Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80));
+
+    LayerFE::LayerSettings mShadowSettings;
+    mShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+    mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
+    mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+    Region accumClearRegion(Rect(10, 11, 12, 13));
+    compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{
+            Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */
+            false,                                                    /* identity transform */
+            false,                                                    /* needs filtering */
+            false,                                                    /* secure */
+            false, /* supports protected content */
+            accumClearRegion,
+            kDisplayViewport,
+            kDisplayDataspace,
+            true /* realContentIsVisible */,
+            false /* clearContent */,
+    };
+
+    EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+    EXPECT_CALL(mLayers[2].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer2Settings))))
+            .WillOnce(Return(std::vector<LayerFE::LayerSettings>(
+                    {mShadowSettings, mLayers[2].mLayerSettings})));
+
+    auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+                                                              accumClearRegion, kDisplayDataspace);
+    ASSERT_EQ(2u, requests.size());
+
+    EXPECT_EQ(mShadowSettings, requests[0]);
+    EXPECT_EQ(mLayers[2].mLayerSettings, requests[1]);
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
deleted file mode 100644
index d4c76bc..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 <android-base/stringprintf.h>
-#include <gmock/gmock.h>
-
-namespace {
-
-using android::base::StringAppendF;
-using Rect = android::Rect;
-
-void dumpRect(const Rect& rect, std::string& result, const char* name) {
-    StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom);
-}
-
-// Checks for a region match
-MATCHER_P(RectEq, expected, "") {
-    std::string buf;
-    buf.append("Rects are not equal\n");
-    dumpRect(expected, buf, "expected rect");
-    dumpRect(arg, buf, "actual rect");
-    *result_listener << buf;
-
-    return (expected.left == arg.left) && (expected.top == arg.top) &&
-            (expected.right == arg.right) && (expected.bottom == arg.bottom);
-}
-
-} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
index 5a4efa9..2adde23 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -20,24 +20,22 @@
 
 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;
+using Region = android::Region;
 
-    size_t expectedRectCount = 0;
-    android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
-    size_t actualRectCount = 0;
-    android::Rect const* actualRects = arg.getArray(&actualRectCount);
+struct RegionMatcher : public testing::MatcherInterface<const Region&> {
+    const Region expected;
 
-    if (expectedRectCount != actualRectCount) return false;
-    for (size_t i = 0; i < expectedRectCount; i++) {
-        if (expectedRects[i] != actualRects[i]) return false;
+    explicit RegionMatcher(const Region& expectedValue) : expected(expectedValue) {}
+
+    bool MatchAndExplain(const Region& actual, testing::MatchResultListener*) const override {
+        return expected.hasSameRects(actual);
     }
-    return true;
+
+    void DescribeTo(::std::ostream* os) const override { PrintTo(expected, os); }
+};
+
+testing::Matcher<const Region&> RegionEq(const Region& expected) {
+    return MakeMatcher(new RegionMatcher(expected));
 }
 
 } // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index f75a4dc..fd47e45 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -18,6 +18,7 @@
 #include <cstdint>
 
 #include <compositionengine/RenderSurfaceCreationArgs.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/impl/RenderSurface.h>
 #include <compositionengine/mock/CompositionEngine.h>
 #include <compositionengine/mock/Display.h>
@@ -27,15 +28,9 @@
 #include <gtest/gtest.h>
 #include <renderengine/mock/RenderEngine.h>
 
-#include "MockHWComposer.h"
-
 namespace android::compositionengine {
 namespace {
 
-/* ------------------------------------------------------------------------
- * 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});
@@ -55,14 +50,11 @@
     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));
         EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL))
                 .WillRepeatedly(Return(NO_ERROR));
     }
-    ~RenderSurfaceTest() override = default;
 
-    StrictMock<android::mock::HWComposer> mHwComposer;
     StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
     StrictMock<mock::CompositionEngine> mCompositionEngine;
     StrictMock<mock::Display> mDisplay;
@@ -74,7 +66,7 @@
                                                            mDisplaySurface}};
 };
 
-/* ------------------------------------------------------------------------
+/*
  * Basic construction
  */
 
@@ -82,7 +74,7 @@
     EXPECT_TRUE(mSurface.isValid());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::initialize()
  */
 
@@ -95,7 +87,7 @@
     mSurface.initialize();
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::getSize()
  */
 
@@ -105,7 +97,7 @@
     EXPECT_EQ(expected, mSurface.getSize());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::getClientTargetAcquireFence()
  */
 
@@ -117,7 +109,7 @@
     EXPECT_EQ(fence.get(), mSurface.getClientTargetAcquireFence().get());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::setDisplaySize()
  */
 
@@ -127,7 +119,7 @@
     mSurface.setDisplaySize(ui::Size(640, 480));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::setBufferDataspace()
  */
 
@@ -138,7 +130,7 @@
     mSurface.setBufferDataspace(ui::Dataspace::DISPLAY_P3);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::setProtected()
  */
 
@@ -179,7 +171,7 @@
     EXPECT_FALSE(mSurface.isProtected());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::beginFrame()
  */
 
@@ -189,73 +181,39 @@
     EXPECT_EQ(NO_ERROR, mSurface.beginFrame(true));
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::prepareFrame()
  */
 
-TEST_F(RenderSurfaceTest, prepareFramePassesOutputLayersToHwc) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(INVALID_OPERATION));
-
-    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
-}
-
-TEST_F(RenderSurfaceTest, prepareFrameTakesEarlyOutOnHwcError) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .WillOnce(Return(INVALID_OPERATION));
-
-    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
-}
-
 TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .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));
+            .WillOnce(Return(NO_ERROR));
 
-    EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame());
+    mSurface.prepareFrame(true, true);
 }
 
-TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .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))
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
+    EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
             .WillOnce(Return(NO_ERROR));
 
-    EXPECT_EQ(NO_ERROR, mSurface.prepareFrame());
+    mSurface.prepareFrame(true, false);
 }
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .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());
+    mSurface.prepareFrame(false, true);
 }
 
 TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
-    EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay)))
-            .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());
+    mSurface.prepareFrame(false, false);
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::dequeueBuffer()
  */
 
@@ -272,7 +230,7 @@
     EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::queueBuffer()
  */
 
@@ -280,9 +238,11 @@
     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));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = false;
+    state.flipClientTarget = false;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
 
     mSurface.queueBuffer(base::unique_fd());
@@ -294,7 +254,11 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer();
     mSurface.mutableGraphicBufferForTest() = buffer;
 
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = true;
+    state.flipClientTarget = false;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
@@ -308,8 +272,11 @@
     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));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = false;
+    state.flipClientTarget = true;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
             .WillOnce(Return(NO_ERROR));
     EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1);
@@ -322,8 +289,11 @@
 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));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = false;
+    state.flipClientTarget = true;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _))
             .WillOnce(
                     DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR)));
@@ -340,7 +310,10 @@
     sp<GraphicBuffer> buffer = new GraphicBuffer();
     mSurface.mutableGraphicBufferForTest() = buffer;
 
-    EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true));
+    impl::OutputCompositionState state;
+    state.usesClientComposition = true;
+
+    EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state));
     EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1))
             .WillOnce(Return(INVALID_OPERATION));
     EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true));
@@ -353,7 +326,7 @@
     EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get());
 }
 
-/* ------------------------------------------------------------------------
+/*
  * RenderSurface::onPresentDisplayCompleted()
  */
 
@@ -363,21 +336,7 @@
     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()
  */
 
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
deleted file mode 100644
index ea07bed..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 e33bedb..841e79f 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "ContainerLayer"
@@ -26,16 +30,19 @@
 
 ContainerLayer::~ContainerLayer() = default;
 
-bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&, const bool,
-                                        renderengine::LayerSettings&) {
-    return false;
-}
-
 bool ContainerLayer::isVisible() const {
     return false;
 }
 
+sp<Layer> ContainerLayer::createClone() {
+    sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer(
+            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
+                              LayerMetadata()));
+    layer->setInitialValuesForClone(this);
+    return layer;
+}
 
-void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&,
-                                     const Rect&, int32_t, const ui::Dataspace) {}
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index e6dbfcc..9b7bab1 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -28,22 +28,14 @@
     explicit ContainerLayer(const LayerCreationArgs&);
     ~ContainerLayer() override;
 
-    const char* getTypeId() const override { return "ContainerLayer"; }
+    const char* getType() const override { return "ContainerLayer"; }
     bool isVisible() const override;
 
-    void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform,
-                         const Rect& viewport, int32_t supportedPerFrameMetadata,
-                         const ui::Dataspace targetDataspace) 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,
-                            const bool supportProtectedContent,
-                            renderengine::LayerSettings& layer) override;
+    bool canDrawShadows() const override { return false; }
+    sp<Layer> createClone() override;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 5700d72..cd6bbd1 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "DisplayDevice"
@@ -41,30 +45,24 @@
 
 using android::base::StringAppendF;
 
-/*
- * Initialize the display to the specified values.
- *
- */
-
-uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0;
+ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
 
 DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
                                                      const wp<IBinder>& displayToken,
-                                                     const std::optional<DisplayId>& displayId)
+                                                     std::optional<DisplayId> displayId)
       : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
 
 DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
       : mFlinger(args.flinger),
         mDisplayToken(args.displayToken),
         mSequenceId(args.sequenceId),
-        mDisplayInstallOrientation(args.displayInstallOrientation),
+        mConnectionType(args.connectionType),
         mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
-                compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual,
-                                                       args.displayId})},
-        mIsVirtual(args.isVirtual),
-        mOrientation(),
-        mActiveConfig(0),
+                compositionengine::DisplayCreationArgs{args.isVirtual(), args.displayId,
+                                                       args.powerAdvisor})},
+        mPhysicalOrientation(args.physicalOrientation),
         mIsPrimary(args.isPrimary) {
+    mCompositionDisplay->editState().isSecure = args.isSecure;
     mCompositionDisplay->createRenderSurface(
             compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth(
                                                                  args.nativeWindow.get()),
@@ -72,6 +70,12 @@
                                                                  args.nativeWindow.get()),
                                                          args.nativeWindow, args.displaySurface});
 
+    if (!mFlinger->mDisableClientCompositionCache &&
+        SurfaceFlinger::maxFrameBufferAcquiredBuffers > 0) {
+        mCompositionDisplay->createClientCompositionCache(
+                static_cast<uint32_t>(SurfaceFlinger::maxFrameBufferAcquiredBuffers));
+    }
+
     mCompositionDisplay->createDisplayColorProfile(
             compositionengine::DisplayColorProfileCreationArgs{args.hasWideColorGamut,
                                                                std::move(args.hdrCapabilities),
@@ -87,7 +91,7 @@
     setPowerMode(args.initialPowerMode);
 
     // initialize the display orientation transform.
-    setProjection(DisplayState::eOrientationDefault, Rect::INVALID_RECT, Rect::INVALID_RECT);
+    setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT);
 }
 
 DisplayDevice::~DisplayDevice() = default;
@@ -117,24 +121,6 @@
 }
 
 // ----------------------------------------------------------------------------
-
-void DisplayDevice::setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers) {
-    mVisibleLayersSortedByZ = layers;
-}
-
-const Vector< sp<Layer> >& DisplayDevice::getVisibleLayersSortedByZ() const {
-    return mVisibleLayersSortedByZ;
-}
-
-void DisplayDevice::setLayersNeedingFences(const Vector< sp<Layer> >& layers) {
-    mLayersNeedingFences = layers;
-}
-
-const Vector< sp<Layer> >& DisplayDevice::getLayersNeedingFences() const {
-    return mLayersNeedingFences;
-}
-
-// ----------------------------------------------------------------------------
 void DisplayDevice::setPowerMode(int mode) {
     mPowerMode = mode;
     getCompositionDisplay()->setCompositionEnabled(mPowerMode != HWC_POWER_MODE_OFF);
@@ -148,62 +134,27 @@
     return mPowerMode != HWC_POWER_MODE_OFF;
 }
 
-// ----------------------------------------------------------------------------
-void DisplayDevice::setActiveConfig(int mode) {
+void DisplayDevice::setActiveConfig(HwcConfigIndexType mode) {
     mActiveConfig = mode;
 }
 
-int DisplayDevice::getActiveConfig()  const {
+HwcConfigIndexType DisplayDevice::getActiveConfig() const {
     return mActiveConfig;
 }
 
-// ----------------------------------------------------------------------------
-
 ui::Dataspace DisplayDevice::getCompositionDataSpace() const {
     return mCompositionDisplay->getState().dataspace;
 }
 
-// ----------------------------------------------------------------------------
-
-void DisplayDevice::setLayerStack(uint32_t stack) {
+void DisplayDevice::setLayerStack(ui::LayerStack stack) {
     mCompositionDisplay->setLayerStackFilter(stack, isPrimary());
 }
 
-// ----------------------------------------------------------------------------
-
-uint32_t DisplayDevice::displayStateOrientationToTransformOrientation(int orientation) {
-    switch (orientation) {
-    case DisplayState::eOrientationDefault:
-        return ui::Transform::ROT_0;
-    case DisplayState::eOrientation90:
-        return ui::Transform::ROT_90;
-    case DisplayState::eOrientation180:
-        return ui::Transform::ROT_180;
-    case DisplayState::eOrientation270:
-        return ui::Transform::ROT_270;
-    default:
-        return ui::Transform::ROT_INVALID;
-    }
+void DisplayDevice::setDisplaySize(int width, int height) {
+    mCompositionDisplay->setBounds(ui::Size(width, height));
 }
 
-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);
-    return NO_ERROR;
-}
-
-void DisplayDevice::setDisplaySize(const int newWidth, const int newHeight) {
-    mCompositionDisplay->setBounds(ui::Size(newWidth, newHeight));
-}
-
-void DisplayDevice::setProjection(int orientation,
-        const Rect& newViewport, const Rect& newFrame) {
-    Rect viewport(newViewport);
-    Rect frame(newFrame);
-
+void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) {
     mOrientation = orientation;
 
     const Rect& displayBounds = getCompositionDisplay()->getState().bounds;
@@ -211,7 +162,10 @@
     const int h = displayBounds.height();
 
     ui::Transform R;
-    DisplayDevice::orientationToTransfrom(orientation, w, h, &R);
+    if (const auto flags = ui::Transform::toRotationFlags(orientation);
+        flags != ui::Transform::ROT_INVALID) {
+        R.set(flags, w, h);
+    }
 
     if (!frame.isValid()) {
         // the destination frame can be invalid if it has never been set,
@@ -253,9 +207,10 @@
     // need to take care of primary display rotation for globalTransform
     // for case if the panel is not installed aligned with device orientation
     if (isPrimary()) {
-        DisplayDevice::orientationToTransfrom(
-                (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1),
-                w, h, &R);
+        if (const auto flags = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
+            flags != ui::Transform::ROT_INVALID) {
+            R.set(flags, w, h);
+        }
     }
 
     // The viewport and frame are both in the logical orientation.
@@ -267,34 +222,44 @@
     const bool needsFiltering =
             (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE));
 
-    Rect scissor = globalTransform.transform(viewport);
-    if (scissor.isEmpty()) {
-        scissor = displayBounds;
+    Rect sourceClip = globalTransform.transform(viewport);
+    if (sourceClip.isEmpty()) {
+        sourceClip = displayBounds;
     }
+    // For normal display use we always set the source and destination clip
+    // rectangles to the same values.
+    const Rect& destinationClip = sourceClip;
 
     uint32_t transformOrientation;
 
     if (isPrimary()) {
-        sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation);
-        transformOrientation = displayStateOrientationToTransformOrientation(
-                (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1));
+        sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation);
+        transformOrientation = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation);
     } else {
-        transformOrientation = displayStateOrientationToTransformOrientation(orientation);
+        transformOrientation = ui::Transform::toRotationFlags(orientation);
     }
 
-    getCompositionDisplay()->setProjection(globalTransform, transformOrientation,
-                                           frame, viewport, scissor, needsFiltering);
+    getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport,
+                                           sourceClip, destinationClip, needsFiltering);
 }
 
-uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() {
-    return sPrimaryDisplayOrientation;
+ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() {
+    return sPrimaryDisplayRotationFlags;
 }
 
 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());
+    std::string displayId;
+    if (const auto id = getId()) {
+        displayId = to_string(*id) + ", ";
+    }
+
+    const char* type = "virtual";
+    if (mConnectionType) {
+        type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external";
+    }
+
+    return base::StringPrintf("DisplayDevice{%s%s%s, \"%s\"}", displayId.c_str(), type,
+                              isPrimary() ? ", primary" : "", mDisplayName.c_str());
 }
 
 void DisplayDevice::dump(std::string& result) const {
@@ -302,8 +267,7 @@
 
     result.append("   ");
     StringAppendF(&result, "powerMode=%d, ", mPowerMode);
-    StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
-    StringAppendF(&result, "numLayers=%zu\n", mVisibleLayersSortedByZ.size());
+    StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
     getCompositionDisplay()->dump(result);
 }
 
@@ -333,7 +297,7 @@
     return mCompositionDisplay->getState().needsFiltering;
 }
 
-uint32_t DisplayDevice::getLayerStack() const {
+ui::LayerStack DisplayDevice::getLayerStack() const {
     return mCompositionDisplay->getState().layerStackId;
 }
 
@@ -349,8 +313,8 @@
     return mCompositionDisplay->getState().frame;
 }
 
-const Rect& DisplayDevice::getScissor() const {
-    return mCompositionDisplay->getState().scissor;
+const Rect& DisplayDevice::getSourceClip() const {
+    return mCompositionDisplay->getState().sourceClip;
 }
 
 bool DisplayDevice::hasWideColorGamut() const {
@@ -384,3 +348,6 @@
 std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1);
 
 }  // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 0067b50..d970b82 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_DISPLAY_DEVICE_H
-#define ANDROID_DISPLAY_DEVICE_H
-
-#include <stdlib.h>
+#pragma once
 
 #include <memory>
 #include <optional>
@@ -31,6 +28,8 @@
 #include <math/mat4.h>
 #include <renderengine/RenderEngine.h>
 #include <system/window.h>
+#include <ui/DisplayInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
@@ -40,7 +39,9 @@
 #include <utils/Timers.h>
 
 #include "DisplayHardware/DisplayIdentification.h"
+#include "DisplayHardware/PowerAdvisor.h"
 #include "RenderArea.h"
+#include "Scheduler/HwcStrongTypes.h"
 
 namespace android {
 
@@ -52,7 +53,6 @@
 
 struct CompositionInfo;
 struct DisplayDeviceCreationArgs;
-struct DisplayInfo;
 
 namespace compositionengine {
 class Display;
@@ -64,10 +64,6 @@
     constexpr static float sDefaultMinLumiance = 0.0;
     constexpr static float sDefaultMaxLumiance = 500.0;
 
-    enum {
-        NO_LAYER_STACK = 0xFFFFFFFF,
-    };
-
     explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
     virtual ~DisplayDevice();
 
@@ -75,34 +71,34 @@
         return mCompositionDisplay;
     }
 
-    bool isVirtual() const { return mIsVirtual; }
+    std::optional<DisplayConnectionType> getConnectionType() const { return mConnectionType; }
+
+    bool isVirtual() const { return !mConnectionType; }
     bool isPrimary() const { return mIsPrimary; }
 
     // isSecure indicates whether this display can be trusted to display
     // secure surfaces.
     bool isSecure() const;
 
-    int         getWidth() const;
-    int         getHeight() const;
-    int         getInstallOrientation() const { return mDisplayInstallOrientation; }
+    int getWidth() const;
+    int getHeight() const;
+    ui::Size getSize() const { return {getWidth(), getHeight()}; }
 
-    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;
+    void setLayerStack(ui::LayerStack);
+    void setDisplaySize(int width, int height);
+    void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
 
-    void                    setLayerStack(uint32_t stack);
-    void                    setDisplaySize(const int newWidth, const int newHeight);
-    void                    setProjection(int orientation, const Rect& viewport, const Rect& frame);
+    ui::Rotation getPhysicalOrientation() const { return mPhysicalOrientation; }
+    ui::Rotation getOrientation() const { return mOrientation; }
 
-    int                     getOrientation() const { return mOrientation; }
-    static uint32_t         getPrimaryDisplayOrientationTransform();
+    static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags();
+
     const ui::Transform& getTransform() const;
     const Rect& getViewport() const;
     const Rect& getFrame() const;
-    const Rect& getScissor() const;
+    const Rect& getSourceClip() const;
     bool needsFiltering() const;
-    uint32_t getLayerStack() const;
+    ui::LayerStack getLayerStack() const;
 
     const std::optional<DisplayId>& getId() const;
     const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
@@ -148,8 +144,8 @@
     /* ------------------------------------------------------------------------
      * Display active config management.
      */
-    int getActiveConfig() const;
-    void setActiveConfig(int mode);
+    HwcConfigIndexType getActiveConfig() const;
+    void setActiveConfig(HwcConfigIndexType mode);
 
     // release HWC resources (if any) for removable displays
     void disconnect();
@@ -162,58 +158,46 @@
     void dump(std::string& result) const;
 
 private:
-    /*
-     *  Constants, set during initialization
-     */
     const sp<SurfaceFlinger> mFlinger;
     const wp<IBinder> mDisplayToken;
     const int32_t mSequenceId;
+    const std::optional<DisplayConnectionType> mConnectionType;
 
-    const int mDisplayInstallOrientation;
     const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
 
     std::string mDisplayName;
-    const bool mIsVirtual;
 
-    /*
-     * Can only accessed from the main thread, these members
-     * don't need synchronization.
-     */
+    const ui::Rotation mPhysicalOrientation;
+    ui::Rotation mOrientation = ui::ROTATION_0;
 
-    // list of visible layers on that display
-    Vector< sp<Layer> > mVisibleLayersSortedByZ;
-    // list of layers needing fences
-    Vector< sp<Layer> > mLayersNeedingFences;
+    static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags;
 
-    /*
-     * Transaction state
-     */
-    static uint32_t displayStateOrientationToTransformOrientation(int orientation);
-    static status_t orientationToTransfrom(int orientation,
-                                           int w, int h, ui::Transform* tr);
-
-    int mOrientation;
-    static uint32_t sPrimaryDisplayOrientation;
-
-    // Current power mode
-    int mPowerMode;
-    // Current active config
-    int mActiveConfig;
+    int mPowerMode = HWC_POWER_MODE_OFF;
+    HwcConfigIndexType mActiveConfig;
 
     // TODO(b/74619554): Remove special cases for primary display.
     const bool mIsPrimary;
 };
 
 struct DisplayDeviceState {
-    bool isVirtual() const { return !displayId.has_value(); }
+    struct Physical {
+        DisplayId id;
+        DisplayConnectionType type;
+
+        bool operator==(const Physical& other) const {
+            return id == other.id && type == other.type;
+        }
+    };
+
+    bool isVirtual() const { return !physical; }
 
     int32_t sequenceId = sNextSequenceId++;
-    std::optional<DisplayId> displayId;
+    std::optional<Physical> physical;
     sp<IGraphicBufferProducer> surface;
-    uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
+    ui::LayerStack layerStack = ui::NO_LAYER_STACK;
     Rect viewport;
     Rect frame;
-    uint8_t orientation = 0;
+    ui::Rotation orientation = ui::ROTATION_0;
     uint32_t width = 0;
     uint32_t height = 0;
     std::string displayName;
@@ -226,53 +210,61 @@
 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);
+    DisplayDeviceCreationArgs(const sp<SurfaceFlinger>&, const wp<IBinder>& displayToken,
+                              std::optional<DisplayId>);
+
+    bool isVirtual() const { return !connectionType; }
 
     const sp<SurfaceFlinger> flinger;
     const wp<IBinder> displayToken;
     const std::optional<DisplayId> displayId;
 
     int32_t sequenceId{0};
-    bool isVirtual{false};
+    std::optional<DisplayConnectionType> connectionType;
     bool isSecure{false};
     sp<ANativeWindow> nativeWindow;
     sp<compositionengine::DisplaySurface> displaySurface;
-    int displayInstallOrientation{DisplayState::eOrientationDefault};
+    ui::Rotation physicalOrientation{ui::ROTATION_0};
     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};
+    Hwc2::PowerAdvisor* powerAdvisor{nullptr};
 };
 
 class DisplayRenderArea : public RenderArea {
 public:
-    DisplayRenderArea(const sp<const DisplayDevice> device,
-                      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, bool allowSecureLayers = true)
+    DisplayRenderArea(const sp<const DisplayDevice>& display,
+                      RotationFlags rotation = ui::Transform::ROT_0)
+          : DisplayRenderArea(display, display->getBounds(),
+                              static_cast<uint32_t>(display->getWidth()),
+                              static_cast<uint32_t>(display->getHeight()),
+                              display->getCompositionDataSpace(), rotation) {}
+
+    DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth,
+                      uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation,
+                      bool allowSecureLayers = true)
           : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
-                       getDisplayRotation(rotation, device->getInstallOrientation())),
-            mDevice(device),
+                       display->getViewport(),
+                       applyInversePhysicalOrientation(rotation,
+                                                       display->getPhysicalOrientation())),
+            mDisplay(std::move(display)),
             mSourceCrop(sourceCrop),
             mAllowSecureLayers(allowSecureLayers) {}
 
-    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 mAllowSecureLayers && mDevice->isSecure(); }
-    const sp<const DisplayDevice> getDisplayDevice() const override { return mDevice; }
+    const ui::Transform& getTransform() const override { return mDisplay->getTransform(); }
+    Rect getBounds() const override { return mDisplay->getBounds(); }
+    int getHeight() const override { return mDisplay->getHeight(); }
+    int getWidth() const override { return mDisplay->getWidth(); }
+    bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); }
+    sp<const DisplayDevice> getDisplayDevice() const override { return mDisplay; }
 
     bool needsFiltering() const override {
         // check if the projection from the logical display to the physical
         // display needs filtering
-        if (mDevice->needsFiltering()) {
+        if (mDisplay->needsFiltering()) {
             return true;
         }
 
@@ -290,7 +282,7 @@
     Rect getSourceCrop() const override {
         // use the projected display viewport by default.
         if (mSourceCrop.isEmpty()) {
-            return mDevice->getScissor();
+            return mDisplay->getSourceClip();
         }
 
         // Recompute the device transformation for the source crop.
@@ -298,29 +290,15 @@
         ui::Transform translatePhysical;
         ui::Transform translateLogical;
         ui::Transform scale;
-        const Rect& viewport = mDevice->getViewport();
-        const Rect& scissor = mDevice->getScissor();
-        const Rect& frame = mDevice->getFrame();
+        const Rect& viewport = mDisplay->getViewport();
+        const Rect& sourceClip = mDisplay->getSourceClip();
+        const Rect& frame = mDisplay->getFrame();
 
-        const int orientation = mDevice->getInstallOrientation();
-        // 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;
-            default:
-                break;
-        }
+        const auto flags = ui::Transform::toRotationFlags(mDisplay->getPhysicalOrientation());
         rotation.set(flags, getWidth(), getHeight());
+
         translateLogical.set(-viewport.left, -viewport.top);
-        translatePhysical.set(scissor.left, scissor.top);
+        translatePhysical.set(sourceClip.left, sourceClip.top);
         scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
                   frame.getHeight() / float(viewport.getHeight()));
         const ui::Transform finalTransform =
@@ -329,49 +307,44 @@
     }
 
 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;
-        }
+    static RotationFlags applyInversePhysicalOrientation(RotationFlags orientation,
+                                                         ui::Rotation physicalOrientation) {
+        uint32_t inverseRotate90 = 0;
+        uint32_t inverseReflect = 0;
 
-        // 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;
+        switch (physicalOrientation) {
+            case ui::ROTATION_0:
+                return orientation;
+
+            case ui::ROTATION_90:
+                inverseRotate90 = ui::Transform::ROT_90;
+                inverseReflect = ui::Transform::ROT_180;
                 break;
-            case DisplayState::eOrientation180:
-                hw_flip_hv = ui::Transform::ROT_180;
+
+            case ui::ROTATION_180:
+                inverseReflect = ui::Transform::ROT_180;
                 break;
-            case DisplayState::eOrientation270:
-                hw_rot_90 = ui::Transform::ROT_90;
+
+            case ui::ROTATION_270:
+                inverseRotate90 = 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;
+        const uint32_t rotate90 = orientation & ui::Transform::ROT_90;
+        uint32_t reflect = orientation & ui::Transform::ROT_180;
+
+        // Apply reflection for double rotation.
+        if (rotate90 & inverseRotate90) {
+            reflect = ~reflect & ui::Transform::ROT_180;
         }
 
-        return static_cast<ui::Transform::orientation_flags>(
-                (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
+        return static_cast<RotationFlags>((rotate90 ^ inverseRotate90) |
+                                          (reflect ^ inverseReflect));
     }
 
-    const sp<const DisplayDevice> mDevice;
+    const sp<const DisplayDevice> mDisplay;
     const Rect mSourceCrop;
     const bool mAllowSecureLayers;
 };
 
-}; // namespace android
-
-#endif // ANDROID_DISPLAY_DEVICE_H
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 7f47a2e..97eeea2 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -14,12 +14,18 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "HwcComposer"
 
-#include <inttypes.h>
 #include <log/log.h>
 
+#include <algorithm>
+#include <cinttypes>
+
 #include "ComposerHal.h"
 
 #include <composer-command-buffer/2.2/ComposerCommandBuffer.h>
@@ -93,6 +99,7 @@
 
 // assume NO_RESOURCES when Status::isOk returns false
 constexpr Error kDefaultError = Error::NO_RESOURCES;
+constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
 
 template<typename T, typename U>
 T unwrapRet(Return<T>& ret, const U& default_val)
@@ -110,6 +117,7 @@
 
 namespace impl {
 
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize)
     : CommandWriterBase(initialMaxSize) {}
 
@@ -160,6 +168,7 @@
     writeSigned(static_cast<int32_t>(metadata.format));
     write64(metadata.usage);
 }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
 Composer::Composer(const std::string& serviceName)
     : mWriter(kWriterInitialSize),
@@ -171,7 +180,16 @@
         LOG_ALWAYS_FATAL("failed to get hwcomposer service");
     }
 
-    if (sp<IComposer> composer_2_3 = IComposer::castFrom(mComposer)) {
+    if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) {
+        composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) {
+            if (tmpError == V2_4::Error::NONE) {
+                mClient = tmpClient;
+                mClient_2_2 = tmpClient;
+                mClient_2_3 = tmpClient;
+                mClient_2_4 = tmpClient;
+            }
+        });
+    } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) {
         composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) {
             if (tmpError == Error::NONE) {
                 mClient = tmpClient;
@@ -198,12 +216,14 @@
         LOG_ALWAYS_FATAL("failed to create composer client");
     }
 
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     if (mIsUsingVrComposer) {
         sp<IVrComposerClient> vrClient = IVrComposerClient::castFrom(mClient);
         if (vrClient == nullptr) {
             LOG_ALWAYS_FATAL("failed to create vr composer client");
         }
     }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 }
 
 Composer::~Composer() = default;
@@ -231,7 +251,12 @@
 void Composer::registerCallback(const sp<IComposerCallback>& callback)
 {
     android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
-    auto ret = mClient->registerCallback(callback);
+    auto ret = [&]() {
+        if (mClient_2_4) {
+            return mClient_2_4->registerCallback_2_4(callback);
+        }
+        return mClient->registerCallback(callback);
+    }();
     if (!ret.isOk()) {
         ALOGE("failed to register IComposerCallback");
     }
@@ -397,15 +422,28 @@
         IComposerClient::Attribute attribute, int32_t* outValue)
 {
     Error error = kDefaultError;
-    mClient->getDisplayAttribute(display, config, attribute,
-            [&](const auto& tmpError, const auto& tmpValue) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
+    if (mClient_2_4) {
+        mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
+                                             [&](const auto& tmpError, const auto& tmpValue) {
+                                                 error = static_cast<Error>(tmpError);
+                                                 if (error != Error::NONE) {
+                                                     return;
+                                                 }
 
-                *outValue = tmpValue;
-            });
+                                                 *outValue = tmpValue;
+                                             });
+    } else {
+        mClient->getDisplayAttribute(display, config,
+                                     static_cast<V2_1::IComposerClient::Attribute>(attribute),
+                                     [&](const auto& tmpError, const auto& tmpValue) {
+                                         error = tmpError;
+                                         if (error != Error::NONE) {
+                                             return;
+                                         }
+
+                                         *outValue = tmpValue;
+                                     });
+    }
 
     return error;
 }
@@ -452,23 +490,6 @@
     return Error::NONE;
 }
 
-Error Composer::getDisplayType(Display display,
-        IComposerClient::DisplayType* outType)
-{
-    Error error = kDefaultError;
-    mClient->getDisplayType(display,
-            [&](const auto& tmpError, const auto& tmpType) {
-                error = tmpError;
-                if (error != Error::NONE) {
-                    return;
-                }
-
-                *outType = tmpType;
-            });
-
-    return error;
-}
-
 Error Composer::getDozeSupport(Display display, bool* outSupport)
 {
     Error error = kDefaultError;
@@ -565,17 +586,20 @@
         const std::vector<IComposerClient::Rect>& damage)
 {
     mWriter.selectDisplay(display);
+
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     if (mIsUsingVrComposer && target.get()) {
         IVrComposerClient::BufferMetadata metadata = {
-            .width = target->getWidth(),
-            .height = target->getHeight(),
-            .stride = target->getStride(),
-            .layerCount = target->getLayerCount(),
-            .format = static_cast<types::V1_0::PixelFormat>(target->getPixelFormat()),
-            .usage = target->getUsage(),
+                .width = target->getWidth(),
+                .height = target->getHeight(),
+                .stride = target->getStride(),
+                .layerCount = target->getLayerCount(),
+                .format = static_cast<types::V1_2::PixelFormat>(target->getPixelFormat()),
+                .usage = target->getUsage(),
         };
         mWriter.setClientTargetMetadata(metadata);
     }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
     const native_handle_t* handle = nullptr;
     if (target.get()) {
@@ -695,17 +719,20 @@
 {
     mWriter.selectDisplay(display);
     mWriter.selectLayer(layer);
+
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     if (mIsUsingVrComposer && buffer.get()) {
         IVrComposerClient::BufferMetadata metadata = {
-            .width = buffer->getWidth(),
-            .height = buffer->getHeight(),
-            .stride = buffer->getStride(),
-            .layerCount = buffer->getLayerCount(),
-            .format = static_cast<types::V1_0::PixelFormat>(buffer->getPixelFormat()),
-            .usage = buffer->getUsage(),
+                .width = buffer->getWidth(),
+                .height = buffer->getHeight(),
+                .stride = buffer->getStride(),
+                .layerCount = buffer->getLayerCount(),
+                .format = static_cast<types::V1_2::PixelFormat>(buffer->getPixelFormat()),
+                .usage = buffer->getUsage(),
         };
         mWriter.setLayerBufferMetadata(metadata);
     }
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
     const native_handle_t* handle = nullptr;
     if (buffer.get()) {
@@ -823,6 +850,7 @@
     return Error::NONE;
 }
 
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type,
                              uint32_t appId)
 {
@@ -833,6 +861,15 @@
     }
     return Error::NONE;
 }
+#else
+Error Composer::setLayerInfo(Display display, Layer layer, uint32_t, uint32_t) {
+    if (mIsUsingVrComposer) {
+        mWriter.selectDisplay(display);
+        mWriter.selectLayer(layer);
+    }
+    return Error::NONE;
+}
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
 Error Composer::execute()
 {
@@ -1093,23 +1130,6 @@
     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) {
@@ -1167,6 +1187,173 @@
     return mClient_2_3->setDisplayBrightness(display, brightness);
 }
 
+// Composer HAL 2.4
+
+Error Composer::getDisplayCapabilities(Display display,
+                                       std::vector<DisplayCapability>* outCapabilities) {
+    if (!mClient_2_3) {
+        return Error::UNSUPPORTED;
+    }
+
+    V2_4::Error error = kDefaultError_2_4;
+    if (mClient_2_4) {
+        mClient_2_4->getDisplayCapabilities_2_4(display,
+                                                [&](const auto& tmpError, const auto& tmpCaps) {
+                                                    error = tmpError;
+                                                    if (error != V2_4::Error::NONE) {
+                                                        return;
+                                                    }
+                                                    *outCapabilities = tmpCaps;
+                                                });
+    } else {
+        mClient_2_3
+                ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) {
+                    error = static_cast<V2_4::Error>(tmpError);
+                    if (error != V2_4::Error::NONE) {
+                        return;
+                    }
+
+                    outCapabilities->resize(tmpCaps.size());
+                    std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(),
+                                   [](auto cap) { return static_cast<DisplayCapability>(cap); });
+                });
+    }
+
+    return static_cast<Error>(error);
+}
+
+V2_4::Error Composer::getDisplayConnectionType(Display display,
+                                               IComposerClient::DisplayConnectionType* outType) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
+        error = tmpError;
+        if (error != V2_4::Error::NONE) {
+            return;
+        }
+
+        *outType = tmpType;
+    });
+
+    return error;
+}
+
+V2_4::Error Composer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getDisplayVsyncPeriod(display,
+                                       [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
+                                           error = tmpError;
+                                           if (error != Error::NONE) {
+                                               return;
+                                           }
+
+                                           *outVsyncPeriod = tmpVsyncPeriod;
+                                       });
+
+    return error;
+}
+
+V2_4::Error Composer::setActiveConfigWithConstraints(
+        Display display, Config config,
+        const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+        VsyncPeriodChangeTimeline* outTimeline) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
+                                                [&](const auto& tmpError, const auto& tmpTimeline) {
+                                                    error = tmpError;
+                                                    if (error != Error::NONE) {
+                                                        return;
+                                                    }
+
+                                                    *outTimeline = tmpTimeline;
+                                                });
+
+    return error;
+}
+
+V2_4::Error Composer::setAutoLowLatencyMode(Display display, bool on) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    return mClient_2_4->setAutoLowLatencyMode(display, on);
+}
+
+V2_4::Error Composer::getSupportedContentTypes(
+        Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getSupportedContentTypes(displayId,
+                                          [&](const auto& tmpError,
+                                              const auto& tmpSupportedContentTypes) {
+                                              error = tmpError;
+                                              if (error != Error::NONE) {
+                                                  return;
+                                              }
+
+                                              *outSupportedContentTypes = tmpSupportedContentTypes;
+                                          });
+    return error;
+}
+
+V2_4::Error Composer::setContentType(Display display, IComposerClient::ContentType contentType) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+
+    return mClient_2_4->setContentType(display, contentType);
+}
+
+V2_4::Error Composer::setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                              bool mandatory, const std::vector<uint8_t>& value) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+    mWriter.selectDisplay(display);
+    mWriter.selectLayer(layer);
+    mWriter.setLayerGenericMetadata(key, mandatory, value);
+    return Error::NONE;
+}
+
+V2_4::Error Composer::getLayerGenericMetadataKeys(
+        std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
+    using Error = V2_4::Error;
+    if (!mClient_2_4) {
+        return Error::UNSUPPORTED;
+    }
+    Error error = kDefaultError_2_4;
+    mClient_2_4->getLayerGenericMetadataKeys([&](const auto& tmpError, const auto& tmpKeys) {
+        error = tmpError;
+        if (error != Error::NONE) {
+            return;
+        }
+
+        *outKeys = tmpKeys;
+    });
+    return error;
+}
+
 CommandReader::~CommandReader()
 {
     resetData();
@@ -1180,8 +1367,7 @@
     uint16_t length = 0;
 
     while (!isEmpty()) {
-        auto command_2_1 = reinterpret_cast<V2_1::IComposerClient::Command*>(&command);
-        if (!beginCommand(command_2_1, &length)) {
+        if (!beginCommand(&command, &length)) {
             break;
         }
 
@@ -1208,6 +1394,9 @@
         case IComposerClient::Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
             parsed = parseSetPresentOrValidateDisplayResult(length);
             break;
+        case IComposerClient::Command::SET_CLIENT_TARGET_PROPERTY:
+            parsed = parseSetClientTargetProperty(length);
+            break;
         default:
             parsed = false;
             break;
@@ -1345,6 +1534,15 @@
     return true;
 }
 
+bool CommandReader::parseSetClientTargetProperty(uint16_t length) {
+    if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) {
+        return false;
+    }
+    mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned());
+    mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned());
+    return true;
+}
+
 void CommandReader::resetData()
 {
     mErrors.clear();
@@ -1469,3 +1667,6 @@
 } // namespace Hwc2
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index c4e952b..aa43f09 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -23,28 +23,40 @@
 #include <utility>
 #include <vector>
 
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
+#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 #include <android/hardware/graphics/common/1.1/types.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 <android/hardware/graphics/composer/2.4/IComposer.h>
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
 #include <ui/DisplayedFrameStats.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
 namespace android {
 
 namespace Hwc2 {
 
-using frameworks::vr::composer::V1_0::IVrComposerClient;
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
+using frameworks::vr::composer::V2_0::IVrComposerClient;
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
 namespace types = hardware::graphics::common;
 
 namespace V2_1 = hardware::graphics::composer::V2_1;
 namespace V2_2 = hardware::graphics::composer::V2_2;
 namespace V2_3 = hardware::graphics::composer::V2_3;
+namespace V2_4 = hardware::graphics::composer::V2_4;
 
 using types::V1_0::ColorTransform;
 using types::V1_0::Transform;
@@ -57,12 +69,14 @@
 using V2_1::Config;
 using V2_1::Display;
 using V2_1::Error;
-using V2_1::IComposerCallback;
 using V2_1::Layer;
-using V2_3::CommandReaderBase;
-using V2_3::CommandWriterBase;
-using V2_3::IComposer;
-using V2_3::IComposerClient;
+using V2_4::CommandReaderBase;
+using V2_4::CommandWriterBase;
+using V2_4::IComposer;
+using V2_4::IComposerCallback;
+using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
 using DisplayCapability = IComposerClient::DisplayCapability;
 using PerFrameMetadata = IComposerClient::PerFrameMetadata;
 using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
@@ -114,7 +128,6 @@
                                      std::vector<Layer>* outLayers,
                                      std::vector<uint32_t>* outLayerRequestMasks) = 0;
 
-    virtual Error getDisplayType(Display display, IComposerClient::DisplayType* outType) = 0;
     virtual Error getDozeSupport(Display display, bool* outSupport) = 0;
     virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes,
                                      float* outMaxLuminance, float* outMaxAverageLuminance,
@@ -199,11 +212,34 @@
                                                    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;
     virtual Error setDisplayBrightness(Display display, float brightness) = 0;
+
+    // Composer HAL 2.4
+    virtual bool isVsyncPeriodSwitchSupported() = 0;
+    virtual Error getDisplayCapabilities(Display display,
+                                         std::vector<DisplayCapability>* outCapabilities) = 0;
+    virtual V2_4::Error getDisplayConnectionType(
+            Display display, IComposerClient::DisplayConnectionType* outType) = 0;
+    virtual V2_4::Error getDisplayVsyncPeriod(Display display,
+                                              VsyncPeriodNanos* outVsyncPeriod) = 0;
+    virtual V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) = 0;
+
+    virtual V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) = 0;
+    virtual V2_4::Error getSupportedContentTypes(
+            Display displayId,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) = 0;
+    virtual V2_4::Error setContentType(Display displayId,
+                                       IComposerClient::ContentType contentType) = 0;
+    virtual V2_4::Error setLayerGenericMetadata(Display display, Layer layer,
+                                                const std::string& key, bool mandatory,
+                                                const std::vector<uint8_t>& value) = 0;
+    virtual V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
 };
 
 namespace impl {
@@ -256,6 +292,7 @@
     bool parseSetPresentFence(uint16_t length);
     bool parseSetReleaseFences(uint16_t length);
     bool parseSetPresentOrValidateDisplayResult(uint16_t length);
+    bool parseSetClientTargetProperty(uint16_t length);
 
     struct ReturnData {
         uint32_t displayRequests = 0;
@@ -272,6 +309,13 @@
         std::vector<int> releaseFences;
 
         uint32_t presentOrValidateState;
+
+        // Composer 2.4 implementation can return a client target property
+        // structure to indicate the client target properties that hardware
+        // composer requests. The composer client must change the client target
+        // properties to match this request.
+        IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
+                                                                   Dataspace::UNKNOWN};
     };
 
     std::vector<CommandError> mErrors;
@@ -330,7 +374,6 @@
                              std::vector<Layer>* outLayers,
                              std::vector<uint32_t>* outLayerRequestMasks) override;
 
-    Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
     Error getDozeSupport(Display display, bool* outSupport) override;
     Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance,
                              float* outMaxAverageLuminance, float* outMinLuminance) override;
@@ -410,14 +453,35 @@
                                            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;
     Error setDisplayBrightness(Display display, float brightness) override;
 
+    // Composer HAL 2.4
+    bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
+    Error getDisplayCapabilities(Display display,
+                                 std::vector<DisplayCapability>* outCapabilities) override;
+    V2_4::Error getDisplayConnectionType(Display display,
+                                         IComposerClient::DisplayConnectionType* outType) override;
+    V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+    V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) override;
+    V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+    V2_4::Error getSupportedContentTypes(
+            Display displayId,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+    V2_4::Error setContentType(Display displayId,
+                               IComposerClient::ContentType contentType) override;
+    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                        bool mandatory, const std::vector<uint8_t>& value) override;
+    V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
+
 private:
+#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
     class CommandWriter : public CommandWriterBase {
     public:
         explicit CommandWriter(uint32_t initialMaxSize);
@@ -433,6 +497,13 @@
         void writeBufferMetadata(
                 const IVrComposerClient::BufferMetadata& metadata);
     };
+#else
+    class CommandWriter : public CommandWriterBase {
+    public:
+        explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {}
+        ~CommandWriter() override {}
+    };
+#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
 
     // Many public functions above simply write a command into the command
     // queue to batch the calls.  validateDisplay and presentDisplay will call
@@ -443,7 +514,8 @@
 
     sp<V2_1::IComposerClient> mClient;
     sp<V2_2::IComposerClient> mClient_2_2;
-    sp<IComposerClient> mClient_2_3;
+    sp<V2_3::IComposerClient> mClient_2_3;
+    sp<IComposerClient> mClient_2_4;
 
     // 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
index ba7818d..277081f 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "DisplayIdentification"
 
@@ -204,3 +208,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index d63cd79..22b268a 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -23,7 +23,7 @@
 #include <string_view>
 #include <vector>
 
-#include <ui/GraphicTypes.h>
+#include <ui/PhysicalDisplayId.h>
 
 namespace android {
 
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 7370b0c..36544b6 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -15,6 +15,10 @@
  ** limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FramebufferSurface"
@@ -193,3 +197,6 @@
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index c463c4e..fc5d441 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 
 #undef LOG_TAG
@@ -42,8 +46,6 @@
 using android::Rect;
 using android::Region;
 using android::sp;
-using android::hardware::Return;
-using android::hardware::Void;
 
 namespace HWC2 {
 
@@ -60,168 +62,8 @@
     return keys.find(key) != keys.end();
 }
 
-class ComposerCallbackBridge : public Hwc2::IComposerCallback {
-public:
-    ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
-            : mCallback(callback), mSequenceId(sequenceId) {}
-
-    Return<void> onHotplug(Hwc2::Display display,
-                           IComposerCallback::Connection conn) override
-    {
-        HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
-        mCallback->onHotplugReceived(mSequenceId, display, connection);
-        return Void();
-    }
-
-    Return<void> onRefresh(Hwc2::Display display) override
-    {
-        mCallback->onRefreshReceived(mSequenceId, display);
-        return Void();
-    }
-
-    Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
-    {
-        mCallback->onVsyncReceived(mSequenceId, display, timestamp);
-        return Void();
-    }
-
-private:
-    ComposerCallback* mCallback;
-    int32_t mSequenceId;
-};
-
 } // namespace anonymous
 
-
-// Device methods
-
-Device::Device(std::unique_ptr<android::Hwc2::Composer> composer) : mComposer(std::move(composer)) {
-    loadCapabilities();
-}
-
-void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) {
-    if (mRegisteredCallback) {
-        ALOGW("Callback already registered. Ignored extra registration "
-                "attempt.");
-        return;
-    }
-    mRegisteredCallback = true;
-    sp<ComposerCallbackBridge> callbackBridge(
-            new ComposerCallbackBridge(callback, sequenceId));
-    mComposer->registerCallback(callbackBridge);
-}
-
-// Required by HWC2 device
-
-std::string Device::dump() const
-{
-    return mComposer->dumpDebugInfo();
-}
-
-uint32_t Device::getMaxVirtualDisplayCount() const
-{
-    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)
-{
-    ALOGI("Creating virtual display");
-
-    hwc2_display_t displayId = 0;
-    auto intError = mComposer->createVirtualDisplay(width, height,
-            format, &displayId);
-    auto error = static_cast<Error>(intError);
-    if (error != Error::None) {
-        return error;
-    }
-
-    auto display = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, displayId,
-                                                   DisplayType::Virtual);
-    display->setConnected(true);
-    *outDisplay = display.get();
-    mDisplays.emplace(displayId, std::move(display));
-    ALOGI("Created virtual display");
-    return Error::None;
-}
-
-void Device::destroyDisplay(hwc2_display_t displayId)
-{
-    ALOGI("Destroying display %" PRIu64, displayId);
-    mDisplays.erase(displayId);
-}
-
-void Device::onHotplug(hwc2_display_t displayId, Connection connection) {
-    if (connection == Connection::Connected) {
-        // If we get a hotplug connected event for a display we already have,
-        // destroy the display and recreate it. This will force us to requery
-        // the display params and recreate all layers on that display.
-        auto oldDisplay = getDisplayById(displayId);
-        if (oldDisplay != nullptr && oldDisplay->isConnected()) {
-            ALOGI("Hotplug connecting an already connected display."
-                    " Clearing old display state.");
-        }
-        mDisplays.erase(displayId);
-
-        DisplayType displayType;
-        auto intError = mComposer->getDisplayType(displayId,
-                reinterpret_cast<Hwc2::IComposerClient::DisplayType *>(
-                        &displayType));
-        auto error = static_cast<Error>(intError);
-        if (error != Error::None) {
-            ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d). "
-                    "Aborting hotplug attempt.",
-                    displayId, to_string(error).c_str(), intError);
-            return;
-        }
-
-        auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
-                                                          displayId, displayType);
-        newDisplay->setConnected(true);
-        mDisplays.emplace(displayId, std::move(newDisplay));
-    } else if (connection == Connection::Disconnected) {
-        // The display will later be destroyed by a call to
-        // destroyDisplay(). For now we just mark it disconnected.
-        auto display = getDisplayById(displayId);
-        if (display) {
-            display->setConnected(false);
-        } else {
-            ALOGW("Attempted to disconnect unknown display %" PRIu64,
-                  displayId);
-        }
-    }
-}
-
-// Other Device methods
-
-Display* Device::getDisplayById(hwc2_display_t id) {
-    auto iter = mDisplays.find(id);
-    return iter == mDisplays.end() ? nullptr : iter->second.get();
-}
-
-// Device initialization methods
-
-void Device::loadCapabilities()
-{
-    static_assert(sizeof(Capability) == sizeof(int32_t),
-            "Capability size has changed");
-    auto capabilities = mComposer->getCapabilities();
-    for (auto capability : capabilities) {
-        mCapabilities.emplace(static_cast<Capability>(capability));
-    }
-}
-
-Error Device::flushCommands()
-{
-    return static_cast<Error>(mComposer->executeCommands());
-}
-
 // Display methods
 Display::~Display() = default;
 
@@ -253,13 +95,13 @@
 }
 
 namespace impl {
+
 Display::Display(android::Hwc2::Composer& composer,
                  const std::unordered_set<Capability>& capabilities, hwc2_display_t id,
                  DisplayType type)
       : mComposer(composer),
         mCapabilities(capabilities),
         mId(id),
-        mIsConnected(false),
         mType(type) {
     ALOGV("Created display %" PRIu64, id);
 }
@@ -267,20 +109,27 @@
 Display::~Display() {
     mLayers.clear();
 
-    if (mType == DisplayType::Virtual) {
-        ALOGV("Destroying virtual display");
-        auto intError = mComposer.destroyVirtualDisplay(mId);
-        auto error = static_cast<Error>(intError);
-        ALOGE_IF(error != Error::None, "destroyVirtualDisplay(%" PRIu64
-                ") failed: %s (%d)", mId, to_string(error).c_str(), intError);
-    } else if (mType == DisplayType::Physical) {
-        auto error = setVsyncEnabled(HWC2::Vsync::Disable);
-        if (error != Error::None) {
-            ALOGE("~Display: Failed to disable vsync for display %" PRIu64
-                    ": %s (%d)", mId, to_string(error).c_str(),
-                    static_cast<int32_t>(error));
-        }
+    Error error = Error::None;
+    const char* msg;
+    switch (mType) {
+        case DisplayType::Physical:
+            error = setVsyncEnabled(HWC2::Vsync::Disable);
+            msg = "disable VSYNC for";
+            break;
+
+        case DisplayType::Virtual:
+            error = static_cast<Error>(mComposer.destroyVirtualDisplay(mId));
+            msg = "destroy virtual";
+            break;
+
+        case DisplayType::Invalid: // Used in unit tests.
+            break;
     }
+
+    ALOGE_IF(error != Error::None, "%s: Failed to %s display %" PRIu64 ": %s (%d)", __FUNCTION__,
+             msg, mId, to_string(error).c_str(), static_cast<int32_t>(error));
+
+    ALOGV("Destroyed display %" PRIu64, mId);
 }
 
 // Required by HWC2 display
@@ -342,6 +191,36 @@
     return Error::None;
 }
 
+bool Display::isVsyncPeriodSwitchSupported() const {
+    ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
+
+    return mComposer.isVsyncPeriodSwitchSupported();
+}
+
+Error Display::getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const {
+    ALOGV("[%" PRIu64 "] getDisplayVsyncPeriod", mId);
+
+    Error error;
+
+    if (isVsyncPeriodSwitchSupported()) {
+        Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
+        auto intError = mComposer.getDisplayVsyncPeriod(mId, &vsyncPeriodNanos);
+        error = static_cast<Error>(intError);
+        *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
+    } else {
+        // Get the default vsync period
+        hwc2_config_t configId = 0;
+        auto intError_2_1 = mComposer.getActiveConfig(mId, &configId);
+        error = static_cast<Error>(intError_2_1);
+        if (error == Error::None) {
+            auto config = mConfigs.at(configId);
+            *outVsyncPeriod = config->getVsyncPeriod();
+        }
+    }
+
+    return error;
+}
+
 Error Display::getActiveConfigIndex(int* outIndex) const {
     ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
     hwc2_config_t configId = 0;
@@ -357,6 +236,7 @@
     auto pos = mConfigs.find(configId);
     if (pos != mConfigs.end()) {
         *outIndex = std::distance(mConfigs.begin(), pos);
+        ALOGV("[%" PRIu64 "] index = %d", mId, *outIndex);
     } else {
         ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
         // Return no error, but the caller needs to check for a negative index
@@ -499,9 +379,19 @@
     return Error::None;
 }
 
-Error Display::getType(DisplayType* outType) const
-{
-    *outType = mType;
+Error Display::getConnectionType(android::DisplayConnectionType* outType) const {
+    if (mType != DisplayType::Physical) return Error::BadDisplay;
+
+    using ConnectionType = Hwc2::IComposerClient::DisplayConnectionType;
+    ConnectionType connectionType;
+    const auto error = static_cast<Error>(mComposer.getDisplayConnectionType(mId, &connectionType));
+    if (error != Error::None) {
+        return error;
+    }
+
+    *outType = connectionType == ConnectionType::INTERNAL
+            ? android::DisplayConnectionType::Internal
+            : android::DisplayConnectionType::External;
     return Error::None;
 }
 
@@ -594,6 +484,46 @@
     return Error::None;
 }
 
+Error Display::setActiveConfigWithConstraints(
+        const std::shared_ptr<const HWC2::Display::Config>& config,
+        const VsyncPeriodChangeConstraints& constraints, VsyncPeriodChangeTimeline* outTimeline) {
+    ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId);
+    if (config->getDisplayId() != mId) {
+        ALOGE("setActiveConfigWithConstraints received config %u for the wrong display %" PRIu64
+              " (expected %" PRIu64 ")",
+              config->getId(), config->getDisplayId(), mId);
+        return Error::BadConfig;
+    }
+
+    if (isVsyncPeriodSwitchSupported()) {
+        Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
+        hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
+        hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
+
+        Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {};
+        auto intError =
+                mComposer.setActiveConfigWithConstraints(mId, config->getId(), hwc2Constraints,
+                                                         &vsyncPeriodChangeTimeline);
+        outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos;
+        outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired;
+        outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos;
+        return static_cast<Error>(intError);
+    }
+
+    // Use legacy setActiveConfig instead
+    ALOGV("fallback to legacy setActiveConfig");
+    const auto now = systemTime();
+    if (constraints.desiredTimeNanos > now || constraints.seamlessRequired) {
+        ALOGE("setActiveConfigWithConstraints received constraints that can't be satisfied");
+    }
+
+    auto intError_2_4 = mComposer.setActiveConfig(mId, config->getId());
+    outTimeline->newVsyncAppliedTimeNanos = std::max(now, constraints.desiredTimeNanos);
+    outTimeline->refreshRequired = true;
+    outTimeline->refreshTimeNanos = now;
+    return static_cast<Error>(intError_2_4);
+}
+
 Error Display::setActiveConfig(const std::shared_ptr<const Config>& config)
 {
     if (config->getDisplayId() != mId) {
@@ -721,6 +651,26 @@
     return static_cast<Error>(intError);
 }
 
+Error Display::setAutoLowLatencyMode(bool on) const {
+    auto intError = mComposer.setAutoLowLatencyMode(mId, on);
+    return static_cast<Error>(intError);
+}
+
+Error Display::getSupportedContentTypes(std::vector<ContentType>* outSupportedContentTypes) const {
+    std::vector<Hwc2::IComposerClient::ContentType> tmpSupportedContentTypes;
+    auto intError = mComposer.getSupportedContentTypes(mId, &tmpSupportedContentTypes);
+    for (Hwc2::IComposerClient::ContentType contentType : tmpSupportedContentTypes) {
+        outSupportedContentTypes->push_back(static_cast<ContentType>(contentType));
+    }
+    return static_cast<Error>(intError);
+}
+
+Error Display::setContentType(ContentType contentType) const {
+    using Hwc2_ContentType = Hwc2::IComposerClient::ContentType;
+    auto intError = mComposer.setContentType(mId, static_cast<Hwc2_ContentType>(contentType));
+    return static_cast<Error>(intError);
+}
+
 // For use by Device
 
 void Display::setConnected(bool connected) {
@@ -754,12 +704,13 @@
     ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId);
 
     auto config = Config::Builder(*this, configId)
-            .setWidth(getAttribute(configId, Attribute::Width))
-            .setHeight(getAttribute(configId, Attribute::Height))
-            .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
-            .setDpiX(getAttribute(configId, Attribute::DpiX))
-            .setDpiY(getAttribute(configId, Attribute::DpiY))
-            .build();
+                          .setWidth(getAttribute(configId, Attribute::Width))
+                          .setHeight(getAttribute(configId, Attribute::Height))
+                          .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
+                          .setDpiX(getAttribute(configId, Attribute::DpiX))
+                          .setDpiY(getAttribute(configId, Attribute::DpiY))
+                          .setConfigGroup(getAttribute(configId, Attribute::ConfigGroup))
+                          .build();
     mConfigs.emplace(configId, std::move(config));
 }
 
@@ -800,12 +751,11 @@
 
 Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
              hwc2_display_t displayId, hwc2_layer_t layerId)
-  : mComposer(composer),
-    mCapabilities(capabilities),
-    mDisplayId(displayId),
-    mId(layerId),
-    mColorMatrix(android::mat4())
-{
+      : mComposer(composer),
+        mCapabilities(capabilities),
+        mDisplayId(displayId),
+        mId(layerId),
+        mColorMatrix(android::mat4()) {
     ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId);
 }
 
@@ -1045,5 +995,15 @@
     return error;
 }
 
+// Composer HAL 2.4
+Error Layer::setLayerGenericMetadata(const std::string& name, bool mandatory,
+                                     const std::vector<uint8_t>& value) {
+    auto intError = mComposer.setLayerGenericMetadata(mDisplayId, mId, name, mandatory, value);
+    return static_cast<Error>(intError);
+}
+
 } // namespace impl
 } // namespace HWC2
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index b7cdf7f..6549525 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -25,6 +25,7 @@
 
 #include <gui/HdrMetadata.h>
 #include <math/mat4.h>
+#include <ui/DisplayInfo.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Region.h>
@@ -52,8 +53,9 @@
 
 namespace HWC2 {
 
-class Display;
 class Layer;
+using VsyncPeriodChangeConstraints = hwc_vsync_period_change_constraints_t;
+using VsyncPeriodChangeTimeline = hwc_vsync_period_change_timeline_t;
 
 // Implement this interface to receive hardware composer events.
 //
@@ -70,61 +72,16 @@
                                    Connection connection) = 0;
     virtual void onRefreshReceived(int32_t sequenceId,
                                    hwc2_display_t display) = 0;
-    virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
-                                 int64_t timestamp) = 0;
+    virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display, int64_t timestamp,
+                                 std::optional<hwc2_vsync_period_t> vsyncPeriod) = 0;
+    virtual void onVsyncPeriodTimingChangedReceived(
+            int32_t sequenceId, hwc2_display_t display,
+            const hwc_vsync_period_change_timeline_t& updatedTimeline) = 0;
+    virtual void onSeamlessPossible(int32_t sequenceId, hwc2_display_t display) = 0;
+
     virtual ~ComposerCallback() = default;
 };
 
-// C++ Wrapper around hwc2_device_t. Load all functions pointers
-// and handle callback registration.
-class Device
-{
-public:
-    explicit Device(std::unique_ptr<android::Hwc2::Composer> composer);
-
-    void registerCallback(ComposerCallback* callback, int32_t sequenceId);
-
-    // Required by HWC2
-
-    std::string dump() const;
-
-    const std::unordered_set<Capability>& getCapabilities() const {
-        return mCapabilities;
-    };
-
-    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);
-
-    void onHotplug(hwc2_display_t displayId, Connection connection);
-
-    // Other Device methods
-
-    Display* getDisplayById(hwc2_display_t id);
-
-    android::Hwc2::Composer* getComposer() { return mComposer.get(); }
-
-    // We buffer most state changes and flush them implicitly with
-    // Display::validate, Display::present, and Display::presentOrValidate.
-    // This method provides an explicit way to flush state changes to HWC.
-    Error flushCommands();
-
-private:
-    // Initialization methods
-
-    void loadCapabilities();
-
-    // Member variables
-    std::unique_ptr<android::Hwc2::Composer> mComposer;
-    std::unordered_set<Capability> mCapabilities;
-    std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
-    bool mRegisteredCallback = false;
-};
-
 // Convenience C++ class to access hwc2_device_t Display functions directly.
 class Display {
 public:
@@ -170,6 +127,10 @@
                 }
                 return *this;
             }
+            Builder& setConfigGroup(int32_t configGroup) {
+                mConfig->mConfigGroup = configGroup;
+                return *this;
+            }
 
         private:
             float getDefaultDensity();
@@ -184,6 +145,7 @@
         nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
         float getDpiX() const { return mDpiX; }
         float getDpiY() const { return mDpiY; }
+        int32_t getConfigGroup() const { return mConfigGroup; }
 
     private:
         Config(Display& display, hwc2_config_t id);
@@ -196,12 +158,14 @@
         nsecs_t mVsyncPeriod;
         float mDpiX;
         float mDpiY;
+        int32_t mConfigGroup;
     };
 
     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;
+    virtual bool isVsyncPeriodSwitchSupported() const = 0;
 
     [[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
     [[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
@@ -228,7 +192,8 @@
     [[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 getConnectionType(
+            android::DisplayConnectionType*) 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;
@@ -264,6 +229,16 @@
             uint32_t* outNumTypes, uint32_t* outNumRequests,
             android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
     [[clang::warn_unused_result]] virtual Error setDisplayBrightness(float brightness) const = 0;
+    [[clang::warn_unused_result]] virtual Error getDisplayVsyncPeriod(
+            nsecs_t* outVsyncPeriod) const = 0;
+    [[clang::warn_unused_result]] virtual Error setActiveConfigWithConstraints(
+            const std::shared_ptr<const HWC2::Display::Config>& config,
+            const VsyncPeriodChangeConstraints& constraints,
+            VsyncPeriodChangeTimeline* outTimeline) = 0;
+    [[clang::warn_unused_result]] virtual Error setAutoLowLatencyMode(bool on) const = 0;
+    [[clang::warn_unused_result]] virtual Error getSupportedContentTypes(
+            std::vector<HWC2::ContentType>*) const = 0;
+    [[clang::warn_unused_result]] virtual Error setContentType(HWC2::ContentType) const = 0;
 };
 
 namespace impl {
@@ -295,7 +270,7 @@
     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 getConnectionType(android::DisplayConnectionType*) const override;
     Error supportsDoze(bool* outSupport) const override;
     Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override;
     Error getDisplayedContentSamplingAttributes(android::ui::PixelFormat* outFormat,
@@ -323,7 +298,14 @@
     Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
                             android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
     Error setDisplayBrightness(float brightness) const override;
-
+    Error getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const override;
+    Error setActiveConfigWithConstraints(const std::shared_ptr<const HWC2::Display::Config>& config,
+                                         const VsyncPeriodChangeConstraints& constraints,
+                                         VsyncPeriodChangeTimeline* outTimeline) override;
+    Error setAutoLowLatencyMode(bool on) const override;
+    Error getSupportedContentTypes(
+            std::vector<HWC2::ContentType>* outSupportedContentTypes) const override;
+    Error setContentType(HWC2::ContentType contentType) const override;
     // Other Display methods
     hwc2_display_t getId() const override { return mId; }
     bool isConnected() const override { return mIsConnected; }
@@ -331,6 +313,7 @@
     const std::unordered_set<DisplayCapability>& getCapabilities() const override {
         return mDisplayCapabilities;
     };
+    virtual bool isVsyncPeriodSwitchSupported() const override;
 
 private:
     int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -351,14 +334,17 @@
     android::Hwc2::Composer& mComposer;
     const std::unordered_set<Capability>& mCapabilities;
 
-    hwc2_display_t mId;
-    bool mIsConnected;
+    const hwc2_display_t mId;
     DisplayType mType;
+    bool mIsConnected = false;
+
     std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
     std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+
     std::once_flag mDisplayCapabilityQueryFlag;
     std::unordered_set<DisplayCapability> mDisplayCapabilities;
 };
+
 } // namespace impl
 
 class Layer {
@@ -391,6 +377,10 @@
 
     // Composer HAL 2.3
     [[clang::warn_unused_result]] virtual Error setColorTransform(const android::mat4& matrix) = 0;
+
+    // Composer HAL 2.4
+    [[clang::warn_unused_result]] virtual Error setLayerGenericMetadata(
+            const std::string& name, bool mandatory, const std::vector<uint8_t>& value) = 0;
 };
 
 namespace impl {
@@ -399,8 +389,7 @@
 
 class Layer : public HWC2::Layer {
 public:
-    Layer(android::Hwc2::Composer& composer,
-          const std::unordered_set<Capability>& capabilities,
+    Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities,
           hwc2_display_t displayId, hwc2_layer_t layerId);
     ~Layer() override;
 
@@ -429,6 +418,10 @@
     // Composer HAL 2.3
     Error setColorTransform(const android::mat4& matrix) override;
 
+    // Composer HAL 2.4
+    Error setLayerGenericMetadata(const std::string& name, bool mandatory,
+                                  const std::vector<uint8_t>& value) override;
+
 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
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 1099041..784fa74 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -14,12 +14,18 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 
 #undef LOG_TAG
 #define LOG_TAG "HWComposer"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include "HWComposer.h"
+
 #include <compositionengine/Output.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
@@ -29,12 +35,10 @@
 #include <utils/Errors.h>
 #include <utils/Trace.h>
 
-#include "HWComposer.h"
-#include "HWC2.h"
-#include "ComposerHal.h"
-
-#include "../Layer.h"           // needed only for debugging
+#include "../Layer.h" // needed only for debugging
 #include "../SurfaceFlinger.h"
+#include "ComposerHal.h"
+#include "HWC2.h"
 
 #define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \
     ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg)
@@ -65,27 +69,114 @@
 #define RETURN_IF_HWC_ERROR(error, displayId, ...) \
     RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__)
 
+namespace {
+
+using android::hardware::Return;
+using android::hardware::Void;
+
+class ComposerCallbackBridge : public android::Hwc2::IComposerCallback {
+public:
+    ComposerCallbackBridge(HWC2::ComposerCallback* callback, int32_t sequenceId,
+                           bool vsyncSwitchingSupported)
+          : mCallback(callback),
+            mSequenceId(sequenceId),
+            mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+
+    android::hardware::Return<void> onHotplug(
+            android::Hwc2::Display display,
+            android::Hwc2::IComposerCallback::Connection conn) override {
+        HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
+        mCallback->onHotplugReceived(mSequenceId, display, connection);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onRefresh(android::Hwc2::Display display) override {
+        mCallback->onRefreshReceived(mSequenceId, display);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onVsync(android::Hwc2::Display display,
+                                            int64_t timestamp) override {
+        if (!mVsyncSwitchingSupported) {
+            mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+        } else {
+            ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
+        }
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onVsync_2_4(
+            android::Hwc2::Display display, int64_t timestamp,
+            android::Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
+        if (mVsyncSwitchingSupported) {
+            mCallback->onVsyncReceived(mSequenceId, display, timestamp,
+                                       std::make_optional(vsyncPeriodNanos));
+        } else {
+            ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
+        }
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onVsyncPeriodTimingChanged(
+            android::Hwc2::Display display,
+            const android::Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override {
+        hwc_vsync_period_change_timeline_t timeline;
+        timeline.newVsyncAppliedTimeNanos = updatedTimeline.newVsyncAppliedTimeNanos;
+        timeline.refreshRequired = updatedTimeline.refreshRequired;
+        timeline.refreshTimeNanos = updatedTimeline.refreshTimeNanos;
+        mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, timeline);
+        return android::hardware::Void();
+    }
+
+    android::hardware::Return<void> onSeamlessPossible(android::Hwc2::Display display) override {
+        mCallback->onSeamlessPossible(mSequenceId, display);
+        return android::hardware::Void();
+    }
+
+private:
+    HWC2::ComposerCallback* mCallback;
+    const int32_t mSequenceId;
+    const bool mVsyncSwitchingSupported;
+};
+
+} // namespace
+
 namespace android {
 
 HWComposer::~HWComposer() = default;
 
 namespace impl {
 
-HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
-      : mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
+}
+
+HWComposer::HWComposer(const std::string& composerServiceName)
+      : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
+}
 
 HWComposer::~HWComposer() {
     mDisplayData.clear();
 }
 
-void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
-                                  int32_t sequenceId) {
-    mHwcDevice->registerCallback(callback, sequenceId);
+void HWComposer::setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) {
+    loadCapabilities();
+    loadLayerMetadataSupport();
+
+    if (mRegisteredCallback) {
+        ALOGW("Callback already registered. Ignored extra registration attempt.");
+        return;
+    }
+    mRegisteredCallback = true;
+    sp<ComposerCallbackBridge> callbackBridge(
+            new ComposerCallbackBridge(callback, sequenceId,
+                                       mComposer->isVsyncPeriodSwitchSupported()));
+    mComposer->registerCallback(callbackBridge);
 }
 
 bool HWComposer::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
                                               DisplayIdentificationData* outData) const {
-    const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+    const auto error = static_cast<HWC2::Error>(
+            mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData));
     if (error != HWC2::Error::None) {
         if (error != HWC2::Error::Unsupported) {
             LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str());
@@ -95,9 +186,8 @@
     return true;
 }
 
-bool HWComposer::hasCapability(HWC2::Capability capability) const
-{
-    return mHwcDevice->getCapabilities().count(capability) > 0;
+bool HWComposer::hasCapability(HWC2::Capability capability) const {
+    return mCapabilities.count(capability) > 0;
 }
 
 bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
@@ -113,31 +203,6 @@
     return mDisplayData.at(*displayId).hwcDisplay->getCapabilities().count(capability) > 0;
 }
 
-void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) {
-    bool valid = true;
-    switch (from) {
-        case HWC2::Composition::Client:
-            valid = false;
-            break;
-        case HWC2::Composition::Device:
-        case HWC2::Composition::SolidColor:
-            valid = (to == HWC2::Composition::Client);
-            break;
-        case HWC2::Composition::Cursor:
-        case HWC2::Composition::Sideband:
-            valid = (to == HWC2::Composition::Client ||
-                    to == HWC2::Composition::Device);
-            break;
-        default:
-            break;
-    }
-
-    if (!valid) {
-        ALOGE("Invalid layer type change: %s --> %s", to_string(from).c_str(),
-                to_string(to).c_str());
-    }
-}
-
 std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId,
                                                                HWC2::Connection connection) {
     std::optional<DisplayIdentificationInfo> info;
@@ -158,13 +223,33 @@
           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[info->id].hwcDisplay = mHwcDevice->getDisplayById(hwcDisplayId);
+        auto& displayData = mDisplayData[info->id];
+        // If we get a hotplug connected event for a display we already have,
+        // destroy the display and recreate it. This will force us to requery
+        // the display params and recreate all layers on that display.
+        if (displayData.hwcDisplay != nullptr && displayData.hwcDisplay->isConnected()) {
+            ALOGI("Hotplug connecting an already connected display."
+                  " Clearing old display state.");
+        }
+        displayData.hwcDisplay.reset();
+        auto newDisplay =
+                std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
+                                                      HWC2::DisplayType::Physical);
+        newDisplay->setConnected(true);
+        displayData.hwcDisplay = std::move(newDisplay);
         mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
+    } else if (connection == HWC2::Connection::Disconnected) {
+        // The display will later be destroyed by a call to
+        // destroyDisplay(). For now we just mark it disconnected.
+        auto& displayData = mDisplayData[info->id];
+        if (displayData.hwcDisplay) {
+            displayData.hwcDisplay->setConnected(false);
+        } else {
+            ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+        }
+        // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
+        // via SurfaceFlinger's onHotplugReceived callback handling
     }
 
     return info;
@@ -222,14 +307,18 @@
               height, SurfaceFlinger::maxVirtualDisplaySize);
         return {};
     }
-    HWC2::Display* display;
-    auto error = mHwcDevice->createVirtualDisplay(width, height, format,
-            &display);
+    hwc2_display_t hwcDisplayId = 0;
+    const auto error = static_cast<HWC2::Error>(
+            mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
     if (error != HWC2::Error::None) {
         ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
         return {};
     }
 
+    auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
+                                                         hwcDisplayId, HWC2::DisplayType::Virtual);
+    display->setConnected(true);
+
     DisplayId displayId;
     if (mFreeVirtualDisplayIds.empty()) {
         displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
@@ -239,7 +328,7 @@
     }
 
     auto& displayData = mDisplayData[displayId];
-    displayData.hwcDisplay = display;
+    displayData.hwcDisplay = std::move(display);
     displayData.isVirtual = true;
 
     --mRemainingHwcVirtualDisplays;
@@ -249,9 +338,8 @@
 HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
     RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
 
-    auto display = mDisplayData[displayId].hwcDisplay;
     HWC2::Layer* layer;
-    auto error = display->createLayer(&layer);
+    auto error = mDisplayData[displayId].hwcDisplay->createLayer(&layer);
     RETURN_IF_HWC_ERROR(error, displayId, nullptr);
     return layer;
 }
@@ -259,8 +347,7 @@
 void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
     RETURN_IF_INVALID_DISPLAY(displayId);
 
-    auto display = mDisplayData[displayId].hwcDisplay;
-    auto error = display->destroyLayer(layer);
+    auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
     RETURN_IF_HWC_ERROR(error, displayId);
 }
 
@@ -272,8 +359,8 @@
     // the refresh period and whatever closest timestamp we have.
     std::lock_guard lock(displayData.lastHwVsyncLock);
     nsecs_t now = systemTime(CLOCK_MONOTONIC);
-    auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
-    return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
+    auto vsyncPeriodNanos = getDisplayVsyncPeriod(displayId);
+    return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
 }
 
 bool HWComposer::isConnected(DisplayId displayId) const {
@@ -316,6 +403,37 @@
     return config;
 }
 
+// Composer 2.4
+
+DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal);
+    const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay;
+
+    DisplayConnectionType type;
+    const auto error = hwcDisplay->getConnectionType(&type);
+
+    const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId
+            ? DisplayConnectionType::Internal
+            : DisplayConnectionType::External;
+
+    RETURN_IF_HWC_ERROR(error, displayId, FALLBACK_TYPE);
+    return type;
+}
+
+bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, false);
+    return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
+}
+
+nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+    RETURN_IF_INVALID_DISPLAY(displayId, 0);
+
+    nsecs_t vsyncPeriodNanos;
+    auto error = mDisplayData.at(displayId).hwcDisplay->getDisplayVsyncPeriod(&vsyncPeriodNanos);
+    RETURN_IF_HWC_ERROR(error, displayId, 0);
+    return vsyncPeriodNanos;
+}
+
 int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, -1);
 
@@ -399,7 +517,9 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::prepare(DisplayId displayId, const compositionengine::Output& output) {
+status_t HWComposer::getDeviceCompositionChanges(
+        DisplayId displayId, bool frameUsesClientComposition,
+        std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
     ATRACE_CALL();
 
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -419,12 +539,8 @@
     // composition.  When there is client composition, since we haven't
     // rendered to the client target yet, we should not attempt to skip
     // validate.
-    //
-    // displayData.hasClientComposition hasn't been updated for this frame.
-    // The check below is incorrect.  We actually rely on HWC here to fall
-    // back to validate when there is any client layer.
     displayData.validateWasSkipped = false;
-    if (!displayData.hasClientComposition) {
+    if (!frameUsesClientComposition) {
         sp<Fence> outPresentFence;
         uint32_t state = UINT32_MAX;
         error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
@@ -449,58 +565,19 @@
         RETURN_IF_HWC_ERROR_FOR("validate", error, displayId, BAD_INDEX);
     }
 
-    std::unordered_map<HWC2::Layer*, HWC2::Composition> changedTypes;
+    android::HWComposer::DeviceRequestedChanges::ChangedTypes changedTypes;
     changedTypes.reserve(numTypes);
     error = hwcDisplay->getChangedCompositionTypes(&changedTypes);
     RETURN_IF_HWC_ERROR_FOR("getChangedCompositionTypes", error, displayId, BAD_INDEX);
 
-    displayData.displayRequests = static_cast<HWC2::DisplayRequest>(0);
-    std::unordered_map<HWC2::Layer*, HWC2::LayerRequest> layerRequests;
+    auto displayRequests = static_cast<HWC2::DisplayRequest>(0);
+    android::HWComposer::DeviceRequestedChanges::LayerRequests layerRequests;
     layerRequests.reserve(numRequests);
-    error = hwcDisplay->getRequests(&displayData.displayRequests,
-            &layerRequests);
+    error = hwcDisplay->getRequests(&displayRequests, &layerRequests);
     RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
 
-    displayData.hasClientComposition = false;
-    displayData.hasDeviceComposition = false;
-    for (auto& outputLayer : output.getOutputLayersOrderedByZ()) {
-        auto& state = outputLayer->editState();
-        LOG_FATAL_IF(!state.hwc.);
-        auto hwcLayer = (*state.hwc).hwcLayer;
-
-        if (auto it = changedTypes.find(hwcLayer.get()); it != changedTypes.end()) {
-            auto newCompositionType = it->second;
-            validateChange(static_cast<HWC2::Composition>((*state.hwc).hwcCompositionType),
-                           newCompositionType);
-            (*state.hwc).hwcCompositionType =
-                    static_cast<Hwc2::IComposerClient::Composition>(newCompositionType);
-        }
-
-        switch ((*state.hwc).hwcCompositionType) {
-            case Hwc2::IComposerClient::Composition::CLIENT:
-                displayData.hasClientComposition = true;
-                break;
-            case Hwc2::IComposerClient::Composition::DEVICE:
-            case Hwc2::IComposerClient::Composition::SOLID_COLOR:
-            case Hwc2::IComposerClient::Composition::CURSOR:
-            case Hwc2::IComposerClient::Composition::SIDEBAND:
-                displayData.hasDeviceComposition = true;
-                break;
-            default:
-                break;
-        }
-
-        state.clearClientTarget = false;
-        if (auto it = layerRequests.find(hwcLayer.get()); it != layerRequests.end()) {
-            auto request = it->second;
-            if (request == HWC2::LayerRequest::ClearClientTarget) {
-                state.clearClientTarget = true;
-            } else {
-                LOG_DISPLAY_ERROR(displayId,
-                                  ("Unknown layer request " + to_string(request)).c_str());
-            }
-        }
-    }
+    outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
+                                               std::move(layerRequests)});
 
     error = hwcDisplay->acceptChanges();
     RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
@@ -508,40 +585,6 @@
     return NO_ERROR;
 }
 
-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.at(*displayId).hasDeviceComposition;
-}
-
-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.at(*displayId).displayRequests) &
-             static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0);
-}
-
-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.at(*displayId).hasClientComposition;
-}
-
 sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
     return mDisplayData.at(displayId).lastPresentFence;
@@ -549,12 +592,13 @@
 
 sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const {
     RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE);
-    auto displayFences = mDisplayData.at(displayId).releaseFences;
-    if (displayFences.count(layer) == 0) {
+    const auto& displayFences = mDisplayData.at(displayId).releaseFences;
+    auto fence = displayFences.find(layer);
+    if (fence == displayFences.end()) {
         ALOGV("getLayerReleaseFence: Release fence not found");
         return Fence::NO_FENCE;
     }
-    return displayFences[layer];
+    return fence->second;
 }
 
 status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
@@ -567,8 +611,8 @@
 
     if (displayData.validateWasSkipped) {
         // explicitly flush all pending commands
-        auto error = mHwcDevice->flushCommands();
-        RETURN_IF_HWC_ERROR_FOR("flushCommands", error, displayId, UNKNOWN_ERROR);
+        auto error = static_cast<HWC2::Error>(mComposer->executeCommands());
+        RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
         RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR);
         return NO_ERROR;
     }
@@ -641,7 +685,9 @@
     return NO_ERROR;
 }
 
-status_t HWComposer::setActiveConfig(DisplayId displayId, size_t configId) {
+status_t HWComposer::setActiveConfigWithConstraints(
+        DisplayId displayId, size_t configId, const HWC2::VsyncPeriodChangeConstraints& constraints,
+        HWC2::VsyncPeriodChangeTimeline* outTimeline) {
     RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
 
     auto& displayData = mDisplayData[displayId];
@@ -650,7 +696,9 @@
         return BAD_INDEX;
     }
 
-    auto error = displayData.hwcDisplay->setActiveConfig(displayData.configMap[configId]);
+    auto error =
+            displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configMap[configId],
+                                                                   constraints, outTimeline);
     RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
     return NO_ERROR;
 }
@@ -679,8 +727,6 @@
     }
 
     const auto hwcDisplayId = displayData.hwcDisplay->getId();
-    mPhysicalDisplayIdMap.erase(hwcDisplayId);
-    mDisplayData.erase(displayId);
 
     // TODO(b/74619554): Select internal/external display from remaining displays.
     if (hwcDisplayId == mInternalHwcDisplayId) {
@@ -688,8 +734,8 @@
     } else if (hwcDisplayId == mExternalHwcDisplayId) {
         mExternalHwcDisplayId.reset();
     }
-
-    mHwcDevice->destroyDisplay(hwcDisplayId);
+    mPhysicalDisplayIdMap.erase(hwcDisplayId);
+    mDisplayData.erase(displayId);
 }
 
 status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
@@ -801,11 +847,50 @@
     return getComposer()->isUsingVrComposer();
 }
 
+status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
+    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::getSupportedContentTypes(
+        DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error =
+            mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
+
+    RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+    return NO_ERROR;
+}
+
+status_t HWComposer::setContentType(DisplayId displayId, HWC2::ContentType contentType) {
+    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+    const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
+    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;
+}
+
+const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const {
+    return mSupportedLayerGenericMetadata;
+}
+
 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());
+    result.append(mComposer->dumpDebugInfo());
 }
 
 std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const {
@@ -871,5 +956,36 @@
                                                                            : "External display"};
 }
 
+void HWComposer::loadCapabilities() {
+    static_assert(sizeof(HWC2::Capability) == sizeof(int32_t), "Capability size has changed");
+    auto capabilities = mComposer->getCapabilities();
+    for (auto capability : capabilities) {
+        mCapabilities.emplace(static_cast<HWC2::Capability>(capability));
+    }
+}
+
+void HWComposer::loadLayerMetadataSupport() {
+    mSupportedLayerGenericMetadata.clear();
+
+    std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey> supportedMetadataKeyInfo;
+    const auto error = mComposer->getLayerGenericMetadataKeys(&supportedMetadataKeyInfo);
+    if (error != hardware::graphics::composer::V2_4::Error::NONE) {
+        ALOGE("%s: %s failed: %s (%d)", __FUNCTION__, "getLayerGenericMetadataKeys",
+              toString(error).c_str(), static_cast<int32_t>(error));
+        return;
+    }
+
+    for (const auto& [name, mandatory] : supportedMetadataKeyInfo) {
+        mSupportedLayerGenericMetadata.emplace(name, mandatory);
+    }
+}
+
+uint32_t HWComposer::getMaxVirtualDisplayCount() const {
+    return mComposer->getMaxVirtualDisplayCount();
+}
+
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index de863b8..41db501 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -27,7 +27,13 @@
 
 #include <android-base/thread_annotations.h>
 #include <ui/Fence.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #include <ui/GraphicTypes.h>
+#pragma clang diagnostic pop
+
 #include <utils/StrongPointer.h>
 #include <utils/Timers.h>
 
@@ -49,11 +55,16 @@
 class Output;
 } // namespace compositionengine
 
+struct KnownHWCGenericLayerMetadata {
+    const char* name;
+    const uint32_t id;
+};
+
 class HWComposer {
 public:
     virtual ~HWComposer();
 
-    virtual void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
+    virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0;
 
     virtual bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
                                               DisplayIdentificationData* outData) const = 0;
@@ -71,8 +82,26 @@
     // Destroy a previously created layer
     virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0;
 
-    // Asks the HAL what it can do
-    virtual status_t prepare(DisplayId displayId, const compositionengine::Output&) = 0;
+    struct DeviceRequestedChanges {
+        using ChangedTypes = std::unordered_map<HWC2::Layer*, HWC2::Composition>;
+        using DisplayRequests = HWC2::DisplayRequest;
+        using LayerRequests = std::unordered_map<HWC2::Layer*, HWC2::LayerRequest>;
+
+        ChangedTypes changedTypes;
+        DisplayRequests displayRequests;
+        LayerRequests layerRequests;
+    };
+
+    // Gets any required composition change requests from the HWC device.
+    //
+    // Note that frameUsesClientComposition must be set correctly based on
+    // whether the current frame appears to use client composition. If it is
+    // false some internal optimizations are allowed to present the display
+    // with fewer handshakes, but this does not work if client composition is
+    // expected.
+    virtual status_t getDeviceCompositionChanges(
+            DisplayId, bool frameUsesClientComposition,
+            std::optional<DeviceRequestedChanges>* outChanges) = 0;
 
     virtual status_t setClientTarget(DisplayId displayId, uint32_t slot,
                                      const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target,
@@ -84,24 +113,12 @@
     // set power mode
     virtual status_t setPowerMode(DisplayId displayId, int mode) = 0;
 
-    // set active config
-    virtual status_t setActiveConfig(DisplayId displayId, size_t configId) = 0;
-
     // Sets a color transform to be applied to the result of composition
     virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
 
     // reset state when an external, non-virtual display is disconnected
     virtual void disconnectDisplay(DisplayId displayId) = 0;
 
-    // does this display have layers handled by HWC
-    virtual bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const = 0;
-
-    // does this display have pending request to flip client target
-    virtual bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const = 0;
-
-    // does this display have layers handled by GLES
-    virtual bool hasClientComposition(const std::optional<DisplayId>& displayId) const = 0;
-
     // get the present fence received from the last call to present.
     virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0;
 
@@ -171,6 +188,21 @@
 
     virtual bool isUsingVrComposer() const = 0;
 
+    // Composer 2.4
+    virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0;
+    virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
+    virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
+    virtual status_t setActiveConfigWithConstraints(
+            DisplayId displayId, size_t configId,
+            const HWC2::VsyncPeriodChangeConstraints& constraints,
+            HWC2::VsyncPeriodChangeTimeline* outTimeline) = 0;
+    virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+    virtual status_t getSupportedContentTypes(
+            DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) = 0;
+    virtual status_t setContentType(DisplayId displayId, HWC2::ContentType contentType) = 0;
+    virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata()
+            const = 0;
+
     // for debugging ----------------------------------------------------------
     virtual void dump(std::string& out) const = 0;
 
@@ -189,10 +221,11 @@
 class HWComposer final : public android::HWComposer {
 public:
     explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
+    explicit HWComposer(const std::string& composerServiceName);
 
     ~HWComposer() override;
 
-    void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
+    void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override;
 
     bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
                                       DisplayIdentificationData* outData) const override;
@@ -210,8 +243,9 @@
     // 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, const compositionengine::Output&) override;
+    status_t getDeviceCompositionChanges(
+            DisplayId, bool frameUsesClientComposition,
+            std::optional<DeviceRequestedChanges>* outChanges) override;
 
     status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence,
                              const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override;
@@ -222,24 +256,12 @@
     // 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;
 
@@ -304,10 +326,24 @@
 
     bool isUsingVrComposer() const override;
 
+    // Composer 2.4
+    DisplayConnectionType getDisplayConnectionType(DisplayId) const override;
+    bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
+    nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
+    status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
+                                            const HWC2::VsyncPeriodChangeConstraints& constraints,
+                                            HWC2::VsyncPeriodChangeTimeline* outTimeline) override;
+    status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
+    status_t getSupportedContentTypes(DisplayId displayId,
+                                      std::vector<HWC2::ContentType>*) override;
+    status_t setContentType(DisplayId displayId, HWC2::ContentType) override;
+
+    const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override;
+
     // for debugging ----------------------------------------------------------
     void dump(std::string& out) const override;
 
-    Hwc2::Composer* getComposer() const override { return mHwcDevice->getComposer(); }
+    Hwc2::Composer* getComposer() const override { return mComposer.get(); }
 
     // TODO(b/74619554): Remove special cases for internal/external display.
     std::optional<hwc2_display_t> getInternalHwcDisplayId() const override {
@@ -325,15 +361,13 @@
     friend TestableSurfaceFlinger;
 
     std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
-
-    static void validateChange(HWC2::Composition from, HWC2::Composition to);
+    void loadCapabilities();
+    void loadLayerMetadataSupport();
+    uint32_t getMaxVirtualDisplayCount() const;
 
     struct DisplayData {
         bool isVirtual = false;
-        bool hasClientComposition = false;
-        bool hasDeviceComposition = false;
-        HWC2::Display* hwcDisplay = nullptr;
-        HWC2::DisplayRequest displayRequests;
+        std::unique_ptr<HWC2::Display> hwcDisplay;
         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 = nullptr;
@@ -355,9 +389,10 @@
 
     std::unordered_map<DisplayId, DisplayData> mDisplayData;
 
-    // This must be destroyed before mDisplayData, because destructor may call back into HWComposer
-    // and look up DisplayData.
-    std::unique_ptr<HWC2::Device> mHwcDevice;
+    std::unique_ptr<android::Hwc2::Composer> mComposer;
+    std::unordered_set<HWC2::Capability> mCapabilities;
+    std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata;
+    bool mRegisteredCallback = false;
 
     std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
     std::optional<hwc2_display_t> mInternalHwcDisplayId;
@@ -366,7 +401,7 @@
 
     std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
     uint32_t mNextVirtualDisplayId = 0;
-    uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
+    uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 4e0e4df..fba3261 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #include "VirtualDisplaySurface.h"
 
@@ -42,13 +46,14 @@
     switch (type) {
         case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
             return "UNKNOWN";
-        case compositionengine::DisplaySurface::COMPOSITION_GLES:
-            return "GLES";
+        case compositionengine::DisplaySurface::COMPOSITION_GPU:
+            return "GPU";
         case compositionengine::DisplaySurface::COMPOSITION_HWC:
             return "HWC";
         case compositionengine::DisplaySurface::COMPOSITION_MIXED:
             return "MIXED";
-        default:                                  return "<INVALID>";
+        default:
+            return "<INVALID>";
     }
 }
 
@@ -92,7 +97,7 @@
     mSinkBufferHeight = sinkHeight;
 
     // Pick the buffer format to request from the sink when not rendering to it
-    // with GLES. If the consumer needs CPU access, use the default format
+    // with GPU. If the consumer needs CPU access, use the default format
     // set by the consumer. Otherwise allow gralloc to decide the format based
     // on usage bits.
     int sinkUsage;
@@ -143,10 +148,10 @@
     mDbgState = DBG_STATE_PREPARED;
 
     mCompositionType = compositionType;
-    if (mForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
+    if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) {
         // Some hardware can do RGB->YUV conversion more efficiently in hardware
         // controlled by HWC than in hardware controlled by the video encoder.
-        // Forcing GLES-composed frames to go through an extra copy by the HWC
+        // Forcing GPU-composed frames to go through an extra copy by the HWC
         // allows the format conversion to happen there, rather than passing RGB
         // directly to the consumer.
         //
@@ -161,18 +166,17 @@
         mDbgLastCompositionType = mCompositionType;
     }
 
-    if (mCompositionType != COMPOSITION_GLES &&
-            (mOutputFormat != mDefaultOutputFormat ||
-             mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
-        // We must have just switched from GLES-only to MIXED or HWC
-        // composition. Stop using the format and usage requested by the GLES
+    if (mCompositionType != COMPOSITION_GPU &&
+        (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
+        // We must have just switched from GPU-only to MIXED or HWC
+        // composition. Stop using the format and usage requested by the GPU
         // driver; they may be suboptimal when HWC is writing to the output
         // buffer. For example, if the output is going to a video encoder, and
         // HWC can write directly to YUV, some hardware can skip a
         // memory-to-memory RGB-to-YUV conversion step.
         //
-        // If we just switched *to* GLES-only mode, we'll change the
-        // format/usage and get a new buffer when the GLES driver calls
+        // If we just switched *to* GPU-only mode, we'll change the
+        // format/usage and get a new buffer when the GPU driver calls
         // dequeueBuffer().
         mOutputFormat = mDefaultOutputFormat;
         mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
@@ -192,17 +196,16 @@
                 "Unexpected advanceFrame() in %s state on HWC frame",
                 dbgStateStr());
     } else {
-        VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
-                "Unexpected advanceFrame() in %s state on GLES/MIXED frame",
-                dbgStateStr());
+        VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE,
+                    "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr());
     }
     mDbgState = DBG_STATE_HWC;
 
     if (mOutputProducerSlot < 0 ||
             (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
         // Last chance bailout if something bad happened earlier. For example,
-        // in a GLES configuration, if the sink disappears then dequeueBuffer
-        // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+        // in a graphics API configuration, if the sink disappears then dequeueBuffer
+        // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
         // will soldier on. So we end up here without a buffer. There should
         // be lots of scary messages in the log just before this.
         VDS_LOGE("advanceFrame: no buffer, bailing out");
@@ -302,9 +305,8 @@
         return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
-            "Unexpected requestBuffer pslot=%d in %s state",
-            pslot, dbgStateStr());
+    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot,
+                dbgStateStr());
 
     *outBuf = mProducerBuffers[pslot];
     return NO_ERROR;
@@ -374,7 +376,7 @@
 
     VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
             "Unexpected dequeueBuffer() in %s state", dbgStateStr());
-    mDbgState = DBG_STATE_GLES;
+    mDbgState = DBG_STATE_GPU;
 
     VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage);
 
@@ -385,18 +387,18 @@
 
         if (mOutputProducerSlot < 0) {
             // Last chance bailout if something bad happened earlier. For example,
-            // in a GLES configuration, if the sink disappears then dequeueBuffer
-            // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+            // in a graphics API configuration, if the sink disappears then dequeueBuffer
+            // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
             // will soldier on. So we end up here without a buffer. There should
             // be lots of scary messages in the log just before this.
             VDS_LOGE("dequeueBuffer: no buffer, bailing out");
             return NO_MEMORY;
         }
 
-        // We already dequeued the output buffer. If the GLES driver wants
+        // We already dequeued the output buffer. If the GPU driver wants
         // something incompatible, we have to cancel and get a new one. This
         // will mean that HWC will see a different output buffer between
-        // prepare and set, but since we're in GLES-only mode already it
+        // prepare and set, but since we're in GPU-only mode already it
         // shouldn't matter.
 
         usage |= GRALLOC_USAGE_HW_COMPOSER;
@@ -458,10 +460,9 @@
         return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
     }
 
-    VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
-            "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
-            dbgStateStr());
-    mDbgState = DBG_STATE_GLES_DONE;
+    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
+                dbgStateStr());
+    mDbgState = DBG_STATE_GPU_DONE;
 
     VDS_LOGV("queueBuffer pslot=%d", pslot);
 
@@ -488,11 +489,11 @@
         mFbFence = mSlots[item.mSlot].mFence;
 
     } else {
-        LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
-                "Unexpected queueBuffer in state %s for compositionType %s",
-                dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
+        LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU,
+                     "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(),
+                     dbgCompositionTypeStr(mCompositionType));
 
-        // Extract the GLES release fence for HWC to acquire
+        // Extract the GPU release fence for HWC to acquire
         int64_t timestamp;
         bool isAutoTimestamp;
         android_dataspace dataSpace;
@@ -517,9 +518,8 @@
         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,
-            dbgStateStr());
+    VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
+                dbgStateStr());
     VDS_LOGV("cancelBuffer pslot=%d", pslot);
     Source source = fbSourceForCompositionType(mCompositionType);
     return mSource[source]->cancelBuffer(
@@ -641,8 +641,8 @@
         return result;
     mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
 
-    // On GLES-only frames, we don't have the right output buffer acquire fence
-    // until after GLES calls queueBuffer(). So here we just set the buffer
+    // On GPU-only frames, we don't have the right output buffer acquire fence
+    // until after GPU 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,
@@ -672,12 +672,18 @@
 
 const char* VirtualDisplaySurface::dbgStateStr() const {
     switch (mDbgState) {
-        case DBG_STATE_IDLE:      return "IDLE";
-        case DBG_STATE_PREPARED:  return "PREPARED";
-        case DBG_STATE_GLES:      return "GLES";
-        case DBG_STATE_GLES_DONE: return "GLES_DONE";
-        case DBG_STATE_HWC:       return "HWC";
-        default:                  return "INVALID";
+        case DBG_STATE_IDLE:
+            return "IDLE";
+        case DBG_STATE_PREPARED:
+            return "PREPARED";
+        case DBG_STATE_GPU:
+            return "GPU";
+        case DBG_STATE_GPU_DONE:
+            return "GPU_DONE";
+        case DBG_STATE_HWC:
+            return "HWC";
+        default:
+            return "INVALID";
     }
 }
 
@@ -692,3 +698,6 @@
 // ---------------------------------------------------------------------------
 } // namespace android
 // ---------------------------------------------------------------------------
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index d6543d1..3cbad8f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -34,23 +34,23 @@
 class HWComposer;
 class IProducerListener;
 
-/* This DisplaySurface implementation supports virtual displays, where GLES
+/* This DisplaySurface implementation supports virtual displays, where GPU
  * and/or HWC compose into a buffer that is then passed to an arbitrary
  * consumer (the sink) running in another process.
  *
  * The simplest case is when the virtual display will never use the h/w
  * composer -- either the h/w composer doesn't support writing to buffers, or
  * there are more virtual displays than it supports simultaneously. In this
- * case, the GLES driver works directly with the output buffer queue, and
+ * case, the GPU driver works directly with the output buffer queue, and
  * calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do
  * nothing.
  *
  * If h/w composer might be used, then each frame will fall into one of three
- * configurations: GLES-only, HWC-only, and MIXED composition. In all of these,
+ * configurations: GPU-only, HWC-only, and MIXED composition. In all of these,
  * we must provide a FB target buffer and output buffer for the HWC set() call.
  *
- * In GLES-only composition, the GLES driver is given a buffer from the sink to
- * render into. When the GLES driver queues the buffer to the
+ * In GPU-only composition, the GPU driver is given a buffer from the sink to
+ * render into. When the GPU driver queues the buffer to the
  * VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of
  * immediately queueing it to the sink. The buffer is used as both the FB
  * target and output buffer for HWC, though on these frames the HWC doesn't
@@ -65,7 +65,7 @@
  *
  * On MIXED frames, things become more complicated, since some h/w composer
  * implementations can't read from and write to the same buffer. This class has
- * an internal BufferQueue that it uses as a scratch buffer pool. The GLES
+ * an internal BufferQueue that it uses as a scratch buffer pool. The GPU
  * driver is given a scratch buffer to render into. When it finishes rendering,
  * the buffer is queued and then immediately acquired by the
  * VirtualDisplaySurface. The scratch buffer is then used as the FB target
@@ -99,7 +99,7 @@
     virtual ~VirtualDisplaySurface();
 
     //
-    // IGraphicBufferProducer interface, used by the GLES driver.
+    // IGraphicBufferProducer interface, used by the GPU driver.
     //
     virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
     virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
@@ -144,7 +144,7 @@
 
     // Both the sink and scratch buffer pools have their own set of slots
     // ("source slots", or "sslot"). We have to merge these into the single
-    // set of slots used by the GLES producer ("producer slots" or "pslot") and
+    // set of slots used by the graphics producer ("producer slots" or "pslot") and
     // internally in the VirtualDisplaySurface. To minimize the number of times
     // a producer slot switches which source it comes from, we map source slot
     // numbers to producer slot numbers differently for each source.
@@ -166,12 +166,12 @@
 
     // To avoid buffer reallocations, we track the buffer usage and format
     // we used on the previous frame and use it again on the new frame. If
-    // the composition type changes or the GLES driver starts requesting
+    // the composition type changes or the GPU driver starts requesting
     // different usage/format, we'll get a new buffer.
     uint32_t mOutputFormat;
     uint64_t mOutputUsage;
 
-    // Since we present a single producer interface to the GLES driver, but
+    // Since we present a single producer interface to the GPU driver, but
     // are internally muxing between the sink and scratch producers, we have
     // to keep track of which source last returned each producer slot from
     // dequeueBuffer. Each bit in mProducerSlotSource corresponds to a producer
@@ -181,7 +181,7 @@
     sp<GraphicBuffer> mProducerBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
 
     // The QueueBufferOutput with the latest info from the sink, and with the
-    // transform hint cleared. Since we defer queueBuffer from the GLES driver
+    // transform hint cleared. Since we defer queueBuffer from the GPU driver
     // to the sink, we have to return the previous version.
     // Moves instead of copies are performed to avoid duplicate
     // FrameEventHistoryDeltas.
@@ -195,7 +195,7 @@
     // Intra-frame state
     //
 
-    // Composition type and GLES buffer source for the current frame.
+    // Composition type and graphics buffer source for the current frame.
     // Valid after prepareFrame(), cleared in onFrameCommitted.
     CompositionType mCompositionType;
 
@@ -221,13 +221,13 @@
     // +-----------+-------------------+-------------+
     // | IDLE      | beginFrame        || BEGUN      |
     // | BEGUN     | prepareFrame      || PREPARED   |
-    // | PREPARED  | dequeueBuffer [1] || GLES       |
+    // | PREPARED  | dequeueBuffer [1] || GPU        |
     // | PREPARED  | advanceFrame [2]  || HWC        |
-    // | GLES      | queueBuffer       || GLES_DONE  |
-    // | GLES_DONE | advanceFrame      || HWC        |
+    // | GPU       | queueBuffer       || GPU_DONE   |
+    // | GPU_DONE  | advanceFrame      || HWC        |
     // | HWC       | onFrameCommitted  || IDLE       |
     // +-----------+-------------------++------------+
-    // [1] COMPOSITION_GLES and COMPOSITION_MIXED frames.
+    // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames.
     // [2] COMPOSITION_HWC frames.
     //
     enum DbgState {
@@ -236,12 +236,12 @@
         // output buffer dequeued, framebuffer source not yet known
         DBG_STATE_BEGUN,
         // output buffer dequeued, framebuffer source known but not provided
-        // to GLES yet.
+        // to GPU yet.
         DBG_STATE_PREPARED,
-        // GLES driver has a buffer dequeued
-        DBG_STATE_GLES,
-        // GLES driver has queued the buffer, we haven't sent it to HWC yet
-        DBG_STATE_GLES_DONE,
+        // GPU driver has a buffer dequeued
+        DBG_STATE_GPU,
+        // GPU driver has queued the buffer, we haven't sent it to HWC yet
+        DBG_STATE_GPU_DONE,
         // HWC has the buffer for this frame
         DBG_STATE_HWC,
     };
diff --git a/services/surfaceflinger/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
new file mode 100644
index 0000000..9d45e33
--- /dev/null
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+// #define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "EffectLayer"
+
+#include "EffectLayer.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/LayerFECompositionState.h>
+#include <renderengine/RenderEngine.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include "DisplayDevice.h"
+#include "SurfaceFlinger.h"
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+EffectLayer::EffectLayer(const LayerCreationArgs& args)
+      : Layer(args),
+        mCompositionState{mFlinger->getCompositionEngine().createLayerFECompositionState()} {}
+
+EffectLayer::~EffectLayer() = default;
+
+std::vector<compositionengine::LayerFE::LayerSettings> EffectLayer::prepareClientCompositionList(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+    std::vector<compositionengine::LayerFE::LayerSettings> results;
+    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+            prepareClientComposition(targetSettings);
+    // Nothing to render.
+    if (!layerSettings) {
+        return {};
+    }
+
+    std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings =
+            prepareShadowClientComposition(*layerSettings, targetSettings.viewport,
+                                           targetSettings.dataspace);
+    if (shadowSettings) {
+        results.push_back(*shadowSettings);
+    }
+
+    // If fill bounds are occluded or the fill color is invalid skip the fill settings.
+    if (targetSettings.realContentIsVisible && fillsColor()) {
+        // Set color for color fill settings.
+        layerSettings->source.solidColor = getColor().rgb;
+        results.push_back(*layerSettings);
+    }
+
+    return results;
+}
+
+bool EffectLayer::isVisible() const {
+    return !isHiddenByPolicy() && getAlpha() > 0.0_hf && hasSomethingToDraw();
+}
+
+bool EffectLayer::setColor(const half3& color) {
+    if (mCurrentState.color.r == color.r && mCurrentState.color.g == color.g &&
+        mCurrentState.color.b == color.b) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.color.r = color.r;
+    mCurrentState.color.g = color.g;
+    mCurrentState.color.b = color.b;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool EffectLayer::setDataspace(ui::Dataspace dataspace) {
+    if (mCurrentState.dataspace == dataspace) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.dataspace = dataspace;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+void EffectLayer::preparePerFrameCompositionState() {
+    Layer::preparePerFrameCompositionState();
+
+    auto* compositionState = editCompositionState();
+    compositionState->color = getColor();
+    compositionState->compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+}
+
+sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const {
+    return asLayerFE();
+}
+
+compositionengine::LayerFECompositionState* EffectLayer::editCompositionState() {
+    return mCompositionState.get();
+}
+
+const compositionengine::LayerFECompositionState* EffectLayer::getCompositionState() const {
+    return mCompositionState.get();
+}
+
+bool EffectLayer::isOpaque(const Layer::State& s) const {
+    // Consider the layer to be opaque if its opaque flag is set or its effective
+    // alpha (considering the alpha of its parents as well) is 1.0;
+    return (s.flags & layer_state_t::eLayerOpaque) != 0 || getAlpha() == 1.0_hf;
+}
+
+ui::Dataspace EffectLayer::getDataSpace() const {
+    return mDrawingState.dataspace;
+}
+
+sp<Layer> EffectLayer::createClone() {
+    sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer(
+            LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
+                              LayerMetadata()));
+    layer->setInitialValuesForClone(this);
+    return layer;
+}
+
+bool EffectLayer::fillsColor() const {
+    return mDrawingState.color.r >= 0.0_hf && mDrawingState.color.g >= 0.0_hf &&
+            mDrawingState.color.b >= 0.0_hf;
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EffectLayer.h b/services/surfaceflinger/EffectLayer.h
new file mode 100644
index 0000000..33758b6
--- /dev/null
+++ b/services/surfaceflinger/EffectLayer.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <sys/types.h>
+
+#include <cstdint>
+
+#include "Layer.h"
+
+namespace android {
+
+// A layer that can render a combination of the following effects.
+//   * fill the bounds of the layer with a color
+//   * render a shadow cast by the bounds of the layer
+// If no effects are enabled, the layer is considered to be invisible.
+class EffectLayer : public Layer {
+public:
+    explicit EffectLayer(const LayerCreationArgs&);
+    ~EffectLayer() override;
+
+    sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override;
+    compositionengine::LayerFECompositionState* editCompositionState() override;
+
+    const char* getType() const override { return "EffectLayer"; }
+    bool isVisible() const override;
+
+    bool setColor(const half3& color) override;
+
+    bool setDataspace(ui::Dataspace dataspace) override;
+
+    ui::Dataspace getDataSpace() const override;
+
+    bool isOpaque(const Layer::State& s) const override;
+
+protected:
+    /*
+     * compositionengine::LayerFE overrides
+     */
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    void preparePerFrameCompositionState() override;
+    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
+            compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) override;
+
+    std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState;
+
+    sp<Layer> createClone() override;
+
+private:
+    // Returns true if there is a valid color to fill.
+    bool fillsColor() const;
+    bool hasSomethingToDraw() const { return fillsColor() || drawShadows(); }
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index 01c9c0f..a7090c5 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "Daltonizer.h"
 #include <math/mat4.h>
 
@@ -171,3 +175,6 @@
 }
 
 } /* namespace android */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
index 365a0bd..3b60952 100644
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ b/services/surfaceflinger/EventLog/EventLog.cpp
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <log/log.h>
-#include <utils/String8.h>
 
 #include "EventLog.h"
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
 
 ANDROID_SINGLETON_STATIC_INSTANCE(EventLog)
 
@@ -31,11 +32,11 @@
 EventLog::EventLog() {
 }
 
-void EventLog::doLogFrameDurations(const String8& window,
-        const int32_t* durations, size_t numDurations) {
+void EventLog::doLogFrameDurations(const std::string_view& name, const int32_t* durations,
+                                   size_t numDurations) {
     EventLog::TagBuffer buffer(LOGTAG_SF_FRAME_DUR);
     buffer.startList(1 + numDurations);
-    buffer.writeString8(window);
+    buffer.writeString(name);
     for (size_t i = 0; i < numDurations; i++) {
         buffer.writeInt32(durations[i]);
     }
@@ -43,10 +44,9 @@
     buffer.log();
 }
 
-void EventLog::logFrameDurations(const String8& window,
-        const int32_t* durations, size_t numDurations) {
-    EventLog::getInstance().doLogFrameDurations(window, durations,
-            numDurations);
+void EventLog::logFrameDurations(const std::string_view& name, const int32_t* durations,
+                                 size_t numDurations) {
+    EventLog::getInstance().doLogFrameDurations(name, durations, numDurations);
 }
 
 // ---------------------------------------------------------------------------
@@ -113,9 +113,9 @@
     mPos += needed;
 }
 
-void EventLog::TagBuffer::writeString8(const String8& value) {
+void EventLog::TagBuffer::writeString(const std::string_view& value) {
     if (mOverflow) return;
-    const int32_t stringLen = value.length();
+    const size_t stringLen = value.length();
     const size_t needed = 1 + sizeof(int32_t) + stringLen;
     if (mPos + needed > STORAGE_MAX_SIZE) {
         mOverflow = true;
@@ -123,11 +123,11 @@
     }
     mStorage[mPos + 0] = EVENT_TYPE_STRING;
     memcpy(&mStorage[mPos + 1], &stringLen, sizeof(int32_t));
-    memcpy(&mStorage[mPos + 5], value.string(), stringLen);
+    memcpy(&mStorage[mPos + 5], value.data(), stringLen);
     mPos += needed;
 }
 
-// ---------------------------------------------------------------------------
-}// namespace android
+} // namespace android
 
-// ---------------------------------------------------------------------------
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h
index efc5d70..ee3587e 100644
--- a/services/surfaceflinger/EventLog/EventLog.h
+++ b/services/surfaceflinger/EventLog/EventLog.h
@@ -14,24 +14,21 @@
  * limitations under the License.
  */
 
-#include <stdint.h>
+#pragma once
+
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
-#ifndef ANDROID_SF_EVENTLOG_H
-#define ANDROID_SF_EVENTLOG_H
+#include <cstdint>
+#include <string_view>
 
-// ---------------------------------------------------------------------------
 namespace android {
-// ---------------------------------------------------------------------------
-
-class String8;
 
 class EventLog : public Singleton<EventLog> {
 
 public:
-    static void logFrameDurations(const String8& window,
-            const int32_t* durations, size_t numDurations);
+    static void logFrameDurations(const std::string_view& name, const int32_t* durations,
+                                  size_t numDurations);
 
 protected:
     EventLog();
@@ -54,18 +51,13 @@
     public:
         explicit TagBuffer(int32_t tag);
 
-        // starts list of items
         void startList(int8_t count);
-        // terminates the list
         void endList();
-        // write a 32-bit integer
-        void writeInt32(int32_t value);
-        // write a 64-bit integer
-        void writeInt64(int64_t value);
-        // write a C string
-        void writeString8(const String8& value);
 
-        // outputs the the buffer to the log
+        void writeInt32(int32_t);
+        void writeInt64(int64_t);
+        void writeString(const std::string_view&);
+
         void log();
     };
 
@@ -74,12 +66,8 @@
     EventLog& operator =(const EventLog&);
 
     enum { LOGTAG_SF_FRAME_DUR = 60100 };
-    void doLogFrameDurations(const String8& window, const int32_t* durations,
-            size_t numDurations);
+    void doLogFrameDurations(const std::string_view& name, const int32_t* durations,
+                             size_t numDurations);
 };
 
-// ---------------------------------------------------------------------------
-}// namespace android
-// ---------------------------------------------------------------------------
-
-#endif /* ANDROID_SF_EVENTLOG_H */
+} // namespace android
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
new file mode 100644
index 0000000..b986f38
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "FrameTracer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "FrameTracer.h"
+
+#include <android-base/stringprintf.h>
+#include <perfetto/trace/clock_snapshot.pbzero.h>
+
+#include <algorithm>
+#include <mutex>
+
+PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::FrameTracer::FrameTracerDataSource);
+
+namespace android {
+
+using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
+void FrameTracer::initialize() {
+    std::call_once(mInitializationFlag, [this]() {
+        perfetto::TracingInitArgs args;
+        args.backends = perfetto::kSystemBackend;
+        perfetto::Tracing::Initialize(args);
+        registerDataSource();
+    });
+}
+
+void FrameTracer::registerDataSource() {
+    perfetto::DataSourceDescriptor dsd;
+    dsd.set_name(kFrameTracerDataSource);
+    FrameTracerDataSource::Register(dsd);
+}
+
+void FrameTracer::traceNewLayer(int32_t layerId, const std::string& layerName) {
+    FrameTracerDataSource::Trace([this, layerId, &layerName](FrameTracerDataSource::TraceContext) {
+        if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
+            std::lock_guard<std::mutex> lock(mTraceMutex);
+            mTraceTracker[layerId].layerName = layerName;
+        }
+    });
+}
+
+void FrameTracer::traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
+                                 nsecs_t timestamp, FrameEvent::BufferEventType type,
+                                 nsecs_t duration) {
+    FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, timestamp, type,
+                                  duration](FrameTracerDataSource::TraceContext ctx) {
+        std::lock_guard<std::mutex> lock(mTraceMutex);
+        if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
+            return;
+        }
+
+        // Handle any pending fences for this buffer.
+        tracePendingFencesLocked(ctx, layerId, bufferID);
+
+        // Complete current trace.
+        traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
+    });
+}
+
+void FrameTracer::traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
+                             const std::shared_ptr<FenceTime>& fence,
+                             FrameEvent::BufferEventType type, nsecs_t startTime) {
+    FrameTracerDataSource::Trace([this, layerId, bufferID, frameNumber, &fence, type,
+                                  startTime](FrameTracerDataSource::TraceContext ctx) {
+        const nsecs_t signalTime = fence->getSignalTime();
+        if (signalTime != Fence::SIGNAL_TIME_INVALID) {
+            std::lock_guard<std::mutex> lock(mTraceMutex);
+            if (mTraceTracker.find(layerId) == mTraceTracker.end()) {
+                return;
+            }
+
+            // Handle any pending fences for this buffer.
+            tracePendingFencesLocked(ctx, layerId, bufferID);
+
+            if (signalTime != Fence::SIGNAL_TIME_PENDING) {
+                traceSpanLocked(ctx, layerId, bufferID, frameNumber, type, startTime, signalTime);
+            } else {
+                mTraceTracker[layerId].pendingFences[bufferID].push_back(
+                        {.frameNumber = frameNumber,
+                         .type = type,
+                         .fence = fence,
+                         .startTime = startTime});
+            }
+        }
+    });
+}
+
+void FrameTracer::tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx,
+                                           int32_t layerId, uint64_t bufferID) {
+    if (mTraceTracker[layerId].pendingFences.count(bufferID)) {
+        auto& pendingFences = mTraceTracker[layerId].pendingFences[bufferID];
+        for (size_t i = 0; i < pendingFences.size(); ++i) {
+            auto& pendingFence = pendingFences[i];
+
+            nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
+            if (pendingFence.fence && pendingFence.fence->isValid()) {
+                signalTime = pendingFence.fence->getSignalTime();
+                if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+                    continue;
+                }
+            }
+
+            if (signalTime != Fence::SIGNAL_TIME_INVALID &&
+                systemTime() - signalTime < kFenceSignallingDeadline) {
+                traceSpanLocked(ctx, layerId, bufferID, pendingFence.frameNumber, pendingFence.type,
+                                pendingFence.startTime, signalTime);
+            }
+
+            pendingFences.erase(pendingFences.begin() + i);
+            --i;
+        }
+    }
+}
+
+void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                              uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
+                              FrameEvent::BufferEventType type, nsecs_t duration) {
+    auto packet = ctx.NewTracePacket();
+    packet->set_timestamp_clock_id(Clock::MONOTONIC);
+    packet->set_timestamp(timestamp);
+    auto* event = packet->set_graphics_frame_event()->set_buffer_event();
+    event->set_buffer_id(static_cast<uint32_t>(bufferID));
+    if (frameNumber != UNSPECIFIED_FRAME_NUMBER) {
+        event->set_frame_number(frameNumber);
+    }
+    event->set_type(type);
+
+    if (mTraceTracker.find(layerId) != mTraceTracker.end() &&
+        !mTraceTracker[layerId].layerName.empty()) {
+        const std::string& layerName = mTraceTracker[layerId].layerName;
+        event->set_layer_name(layerName.c_str(), layerName.size());
+    }
+
+    if (duration > 0) {
+        event->set_duration_ns(duration);
+    }
+}
+
+void FrameTracer::traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                                  uint64_t bufferID, uint64_t frameNumber,
+                                  FrameEvent::BufferEventType type, nsecs_t startTime,
+                                  nsecs_t endTime) {
+    nsecs_t timestamp = endTime;
+    nsecs_t duration = 0;
+    if (startTime > 0 && startTime < endTime) {
+        timestamp = startTime;
+        duration = endTime - startTime;
+    }
+    traceLocked(ctx, layerId, bufferID, frameNumber, timestamp, type, duration);
+}
+
+void FrameTracer::onDestroy(int32_t layerId) {
+    std::lock_guard<std::mutex> traceLock(mTraceMutex);
+    mTraceTracker.erase(layerId);
+}
+
+std::string FrameTracer::miniDump() {
+    std::string result = "FrameTracer miniDump:\n";
+    std::lock_guard<std::mutex> lock(mTraceMutex);
+    android::base::StringAppendF(&result, "Number of layers currently being traced is %zu\n",
+                                 mTraceTracker.size());
+    return result;
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.h b/services/surfaceflinger/FrameTracer/FrameTracer.h
new file mode 100644
index 0000000..ef5df90
--- /dev/null
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.h
@@ -0,0 +1,108 @@
+/*
+ * 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 <perfetto/trace/android/graphics_frame_event.pbzero.h>
+#include <perfetto/tracing.h>
+#include <ui/FenceTime.h>
+
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+
+class FrameTracer {
+public:
+    class FrameTracerDataSource : public perfetto::DataSource<FrameTracerDataSource> {
+        virtual void OnSetup(const SetupArgs&) override{};
+        virtual void OnStart(const StartArgs&) override{};
+        virtual void OnStop(const StopArgs&) override{};
+    };
+
+    static const uint64_t UNSPECIFIED_FRAME_NUMBER = std::numeric_limits<uint64_t>::max();
+
+    using FrameEvent = perfetto::protos::pbzero::GraphicsFrameEvent;
+
+    ~FrameTracer() = default;
+
+    // Sets up the perfetto tracing backend and data source.
+    void initialize();
+    // Registers the data source with the perfetto backend. Called as part of initialize()
+    // and should not be called manually outside of tests. Public to allow for substituting a
+    // perfetto::kInProcessBackend in tests.
+    void registerDataSource();
+    // Starts tracking a new layer for tracing. Needs to be called once before traceTimestamp() or
+    // traceFence() for each layer.
+    void traceNewLayer(int32_t layerId, const std::string& layerName);
+    // Creates a trace point at the timestamp provided.
+    void traceTimestamp(int32_t layerId, uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
+                        FrameEvent::BufferEventType type, nsecs_t duration = 0);
+    // Creates a trace point after the provided fence has been signalled. If a startTime is provided
+    // the trace will have be timestamped from startTime until fence signalling time. If no
+    // startTime is provided, a durationless trace point will be created timestamped at fence
+    // signalling time. If the fence hasn't signalled yet, the trace point will be created the next
+    // time after signalling a trace call for this buffer occurs.
+    void traceFence(int32_t layerId, uint64_t bufferID, uint64_t frameNumber,
+                    const std::shared_ptr<FenceTime>& fence, FrameEvent::BufferEventType type,
+                    nsecs_t startTime = 0);
+
+    // Takes care of cleanup when a layer is destroyed.
+    void onDestroy(int32_t layerId);
+
+    std::string miniDump();
+
+    static constexpr char kFrameTracerDataSource[] = "android.surfaceflinger.frame";
+
+    // The maximum amount of time a fence has to signal before it is discarded.
+    // Used to avoid fences from previous traces generating new trace points in later ones.
+    // Public for testing.
+    static constexpr nsecs_t kFenceSignallingDeadline = 60'000'000'000; // 60 seconds
+
+private:
+    struct PendingFence {
+        uint64_t frameNumber;
+        FrameEvent::BufferEventType type;
+        std::shared_ptr<FenceTime> fence;
+        nsecs_t startTime;
+    };
+
+    struct TraceRecord {
+        std::string layerName;
+        using BufferID = uint64_t;
+        std::unordered_map<BufferID, std::vector<PendingFence>> pendingFences;
+    };
+
+    // Checks if any pending fences for a layer and buffer have signalled and, if they have, creates
+    // trace points for them.
+    void tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                                  uint64_t bufferID);
+    // Creates a trace point by translating a start time and an end time to a timestamp and
+    // duration. If startTime is later than end time it sets end time as the timestamp and the
+    // duration to 0. Used by traceFence().
+    void traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId,
+                         uint64_t bufferID, uint64_t frameNumber, FrameEvent::BufferEventType type,
+                         nsecs_t startTime, nsecs_t endTime);
+    void traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerId, uint64_t bufferID,
+                     uint64_t frameNumber, nsecs_t timestamp, FrameEvent::BufferEventType type,
+                     nsecs_t duration = 0);
+
+    std::mutex mTraceMutex;
+    std::unordered_map<int32_t, TraceRecord> mTraceTracker;
+    std::once_flag mInitializationFlag;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index f4cc49b..8ad805b 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // This is needed for stdint.h to define INT64_MAX in C++
 #define __STDC_LIMIT_MACROS
 
@@ -21,7 +25,6 @@
 
 #include <android-base/stringprintf.h>
 #include <android/log.h>
-#include <utils/String8.h>
 
 #include <ui/FrameStats.h>
 
@@ -139,7 +142,7 @@
     }
 }
 
-void FrameTracker::logAndResetStats(const String8& name) {
+void FrameTracker::logAndResetStats(const std::string_view& name) {
     Mutex::Autolock lock(mMutex);
     logStatsLocked(name);
     resetFrameCountersLocked();
@@ -217,7 +220,7 @@
     }
 }
 
-void FrameTracker::logStatsLocked(const String8& name) const {
+void FrameTracker::logStatsLocked(const std::string_view& name) const {
     for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
         if (mNumFrames[i] > 0) {
             EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS);
@@ -247,3 +250,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index 555dcc1..35382be 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -14,21 +14,18 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_FRAMETRACKER_H
-#define ANDROID_FRAMETRACKER_H
+#pragma once
 
 #include <ui/FenceTime.h>
-
-#include <stddef.h>
-
 #include <utils/Mutex.h>
-#include <utils/Timers.h>
 #include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <cstddef>
+#include <string_view>
 
 namespace android {
 
-class String8;
-
 // FrameTracker tracks information about the most recently rendered frames. It
 // uses a circular buffer of frame records, and is *NOT* thread-safe -
 // mutexing must be done at a higher level if multi-threaded access is
@@ -87,7 +84,7 @@
 
     // logAndResetStats dumps the current statistics to the binary event log
     // and then resets the accumulated statistics to their initial values.
-    void logAndResetStats(const String8& name);
+    void logAndResetStats(const std::string_view& name);
 
     // dumpStats dump appends the current frame display time history to the result string.
     void dumpStats(std::string& result) const;
@@ -123,7 +120,7 @@
     void resetFrameCountersLocked();
 
     // logStatsLocked dumps the current statistics to the binary event log.
-    void logStatsLocked(const String8& name) const;
+    void logStatsLocked(const std::string_view& name) const;
 
     // isFrameValidLocked returns true if the data for the given frame index is
     // valid and has all arrived (i.e. there are no oustanding fences).
@@ -160,6 +157,4 @@
     mutable Mutex mMutex;
 };
 
-}
-
-#endif // ANDROID_FRAMETRACKER_H
+} // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3e6dded..da26a37 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "Layer"
@@ -22,11 +26,10 @@
 #include "Layer.h"
 
 #include <android-base/stringprintf.h>
+#include <binder/IPCThreadState.h>
 #include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
 #include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <cutils/compiler.h>
 #include <cutils/native_handle.h>
@@ -53,10 +56,11 @@
 #include <sstream>
 
 #include "BufferLayer.h"
-#include "ColorLayer.h"
 #include "Colorizer.h"
 #include "DisplayDevice.h"
 #include "DisplayHardware/HWComposer.h"
+#include "EffectLayer.h"
+#include "FrameTracer/FrameTracer.h"
 #include "LayerProtoHelper.h"
 #include "LayerRejecter.h"
 #include "MonitoredProducer.h"
@@ -76,15 +80,11 @@
         mName(args.name),
         mClientRef(args.client),
         mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) {
-    mCurrentCrop.makeInvalid();
-
     uint32_t layerFlags = 0;
     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;
 
-    mTransactionName = String8("TX - ") + mName;
-
     mCurrentState.active_legacy.w = args.w;
     mCurrentState.active_legacy.h = args.h;
     mCurrentState.flags = layerFlags;
@@ -99,18 +99,22 @@
     mCurrentState.active.w = UINT32_MAX;
     mCurrentState.active.h = UINT32_MAX;
     mCurrentState.active.transform.set(0, 0);
+    mCurrentState.frameNumber = 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.surfaceDamageRegion = Region::INVALID_REGION;
     mCurrentState.cornerRadius = 0.0f;
+    mCurrentState.backgroundBlurRadius = 0;
     mCurrentState.api = -1;
     mCurrentState.hasColorTransform = false;
     mCurrentState.colorSpaceAgnostic = false;
+    mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
     mCurrentState.metadata = args.metadata;
+    mCurrentState.shadowRadius = 0.f;
 
     // drawing state & current state are identical
     mDrawingState = mCurrentState;
@@ -120,9 +124,12 @@
     mFrameEventHistory.initializeCompositorTiming(compositorTiming);
     mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval);
 
-    mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType);
+    mCallingPid = args.callingPid;
+    mCallingUid = args.callingUid;
+}
 
-    mFlinger->onLayerCreated();
+void Layer::onFirstRef() {
+    mFlinger->onLayerFirstRef(this);
 }
 
 Layer::~Layer() {
@@ -135,6 +142,21 @@
     mFlinger->onLayerDestroyed(this);
 }
 
+LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client,
+                                     std::string name, uint32_t w, uint32_t h, uint32_t flags,
+                                     LayerMetadata metadata)
+      : flinger(flinger),
+        client(client),
+        name(std::move(name)),
+        w(w),
+        h(h),
+        flags(flags),
+        metadata(std::move(metadata)) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    callingPid = ipc->getCallingPid();
+    callingUid = ipc->getCallingUid();
+}
+
 // ---------------------------------------------------------------------------
 // callbacks
 // ---------------------------------------------------------------------------
@@ -142,7 +164,7 @@
 /*
  * onLayerDisplayed is only meaningful for BufferLayer, but, is called through
  * Layer.  So, the implementation is done in BufferLayer.  When called on a
- * ColorLayer object, it's essentially a NOP.
+ * EffectLayer object, it's essentially a NOP.
  */
 void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {}
 
@@ -219,10 +241,6 @@
 // set-up
 // ---------------------------------------------------------------------------
 
-const String8& Layer::getName() const {
-    return mName;
-}
-
 bool Layer::getPremultipledAlpha() const {
     return mPremultipliedAlpha;
 }
@@ -241,37 +259,6 @@
 // h/w composer set-up
 // ---------------------------------------------------------------------------
 
-bool Layer::hasHwcLayer(const sp<const DisplayDevice>& displayDevice) {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().hwc && (*outputLayer->getState().hwc).hwcLayer != nullptr;
-}
-
-HWC2::Layer* Layer::getHwcLayer(const sp<const DisplayDevice>& displayDevice) {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    if (!outputLayer || !outputLayer->getState().hwc) {
-        return nullptr;
-    }
-    return (*outputLayer->getState().hwc).hwcLayer.get();
-}
-
-Rect Layer::getContentCrop() const {
-    // this is the crop rectangle that applies to the buffer
-    // itself (as opposed to the window)
-    Rect crop;
-    if (!mCurrentCrop.isEmpty()) {
-        // if the buffer crop is defined, we use that
-        crop = mCurrentCrop;
-    } else if (mActiveBuffer != nullptr) {
-        // otherwise we use the whole buffer
-        crop = mActiveBuffer->getBounds();
-    } else {
-        // if we don't have a buffer yet, we use an empty/invalid crop
-        crop.makeInvalid();
-    }
-    return crop;
-}
-
 static Rect reduce(const Rect& win, const Region& exclude) {
     if (CC_LIKELY(exclude.isEmpty())) {
         return win;
@@ -316,7 +303,7 @@
     // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g.
     // it isFixedSize) then there may be additional scaling not accounted
     // for in the layer transform.
-    if (!isFixedSize() || !mActiveBuffer) {
+    if (!isFixedSize() || getBuffer() == nullptr) {
         return {};
     }
 
@@ -328,10 +315,10 @@
         return {};
     }
 
-    int bufferWidth = mActiveBuffer->getWidth();
-    int bufferHeight = mActiveBuffer->getHeight();
+    int bufferWidth = getBuffer()->getWidth();
+    int bufferHeight = getBuffer()->getHeight();
 
-    if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+    if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) {
         std::swap(bufferWidth, bufferHeight);
     }
 
@@ -346,7 +333,7 @@
 ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const {
     // We need to mirror this scaling to child surfaces or we will break the contract where WM can
     // treat child surfaces as pixels in the parent surface.
-    if (!isFixedSize() || !mActiveBuffer) {
+    if (!isFixedSize() || getBuffer() == nullptr) {
         return mEffectiveTransform;
     }
     return mEffectiveTransform * bufferScaleTransform;
@@ -355,13 +342,14 @@
 FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const {
     // We need the pre scaled layer bounds when computing child bounds to make sure the child is
     // cropped to its parent layer after any buffer transform scaling is applied.
-    if (!isFixedSize() || !mActiveBuffer) {
+    if (!isFixedSize() || getBuffer() == nullptr) {
         return mBounds;
     }
     return bufferScaleTransform.inverse().transform(mBounds);
 }
 
-void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) {
+void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
+                          float parentShadowRadius) {
     const State& s(getDrawingState());
 
     // Calculate effective layer transform
@@ -384,11 +372,23 @@
     mBounds = bounds;
     mScreenBounds = mEffectiveTransform.transform(mBounds);
 
+    // Use the layer's own shadow radius if set. Otherwise get the radius from
+    // parent.
+    if (s.shadowRadius > 0.f) {
+        mEffectiveShadowRadius = s.shadowRadius;
+    } else {
+        mEffectiveShadowRadius = parentShadowRadius;
+    }
+
+    // Shadow radius is passed down to only one layer so if the layer can draw shadows,
+    // don't pass it to its children.
+    const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
+
     // Add any buffer scaling to the layer's children.
     ui::Transform bufferScaleTransform = getBufferScaleTransform();
     for (const sp<Layer>& child : mDrawingChildren) {
         child->computeBounds(getBoundsPreScaling(bufferScaleTransform),
-                             getTransformWithScale(bufferScaleTransform));
+                             getTransformWithScale(bufferScaleTransform), childShadowRadius);
     }
 }
 
@@ -413,15 +413,43 @@
     win.bottom -= roundedCornersCrop.top;
 }
 
-void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const {
+void Layer::prepareBasicGeometryCompositionState() {
     const auto& drawingState{getDrawingState()};
-    auto alpha = static_cast<float>(getAlpha());
-    auto blendMode = HWC2::BlendMode::None;
-    if (!isOpaque(drawingState) || alpha != 1.0f) {
-        blendMode =
-                mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage;
+    const uint32_t layerStack = getLayerStack();
+    const auto alpha = static_cast<float>(getAlpha());
+    const bool opaque = isOpaque(drawingState);
+    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+
+    auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
+    if (!opaque || alpha != 1.0f) {
+        blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
+                                        : Hwc2::IComposerClient::BlendMode::COVERAGE;
     }
 
+    auto* compositionState = editCompositionState();
+    compositionState->layerStackId =
+            (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt;
+    compositionState->internalOnly = getPrimaryDisplayOnly();
+    compositionState->isVisible = isVisible();
+    compositionState->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+    compositionState->shadowRadius = mEffectiveShadowRadius;
+
+    compositionState->contentDirty = contentDirty;
+    contentDirty = false;
+
+    compositionState->geomLayerBounds = mBounds;
+    compositionState->geomLayerTransform = getTransform();
+    compositionState->geomInverseLayerTransform = compositionState->geomLayerTransform.inverse();
+    compositionState->transparentRegionHint = getActiveTransparentRegion(drawingState);
+
+    compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+    compositionState->alpha = alpha;
+    compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+}
+
+void Layer::prepareGeometryCompositionState() {
+    const auto& drawingState{getDrawingState()};
+
     int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0);
     int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0);
     sp<Layer> parent = mDrawingParent.promote();
@@ -429,160 +457,261 @@
         auto& parentState = parent->getDrawingState();
         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) {
+        if (parentType > 0 && parentAppId > 0) {
             type = parentType;
             appId = parentAppId;
         }
     }
 
-    compositionState.geomLayerTransform = getTransform();
-    compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse();
-    compositionState.geomBufferSize = getBufferSize(drawingState);
-    compositionState.geomContentCrop = getContentCrop();
-    compositionState.geomCrop = getCrop(drawingState);
-    compositionState.geomBufferTransform = mCurrentTransform;
-    compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
-    compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState);
-    compositionState.geomLayerBounds = mBounds;
-    compositionState.geomUsesSourceCrop = usesSourceCrop();
-    compositionState.isSecure = isSecure();
+    auto* compositionState = editCompositionState();
 
-    compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
-    compositionState.alpha = alpha;
-    compositionState.type = type;
-    compositionState.appId = appId;
+    compositionState->geomBufferSize = getBufferSize(drawingState);
+    compositionState->geomContentCrop = getBufferCrop();
+    compositionState->geomCrop = getCrop(drawingState);
+    compositionState->geomBufferTransform = getBufferTransform();
+    compositionState->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
+    compositionState->geomUsesSourceCrop = usesSourceCrop();
+    compositionState->isSecure = isSecure();
+
+    compositionState->type = type;
+    compositionState->appId = appId;
+
+    compositionState->metadata.clear();
+    const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
+    for (const auto& [key, mandatory] : supportedMetadata) {
+        const auto& genericLayerMetadataCompatibilityMap =
+                mFlinger->getGenericLayerMetadataKeyMap();
+        auto compatIter = genericLayerMetadataCompatibilityMap.find(key);
+        if (compatIter == std::end(genericLayerMetadataCompatibilityMap)) {
+            continue;
+        }
+        const uint32_t id = compatIter->second;
+
+        auto it = drawingState.metadata.mMap.find(id);
+        if (it == std::end(drawingState.metadata.mMap)) {
+            continue;
+        }
+
+        compositionState->metadata
+                .emplace(key, compositionengine::GenericLayerMetadataEntry{mandatory, it->second});
+    }
 }
 
-void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
-                                  bool includeGeometry) const {
-    if (includeGeometry) {
-        latchGeometry(compositionState);
+void Layer::preparePerFrameCompositionState() {
+    const auto& drawingState{getDrawingState()};
+    auto* compositionState = editCompositionState();
+
+    compositionState->forceClientComposition = false;
+
+    compositionState->isColorspaceAgnostic = isColorSpaceAgnostic();
+    compositionState->dataspace = getDataSpace();
+    compositionState->colorTransform = getColorTransform();
+    compositionState->colorTransformIsIdentity = !hasColorTransform();
+    compositionState->surfaceDamage = surfaceDamageRegion;
+    compositionState->hasProtectedContent = isProtected();
+
+    const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f;
+
+    compositionState->isOpaque =
+            isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
+
+    // Force client composition for special cases known only to the front-end.
+    if (isHdrY410() || usesRoundedCorners || drawShadows()) {
+        compositionState->forceClientComposition = true;
+    }
+}
+
+void Layer::prepareCursorCompositionState() {
+    const State& drawingState{getDrawingState()};
+    auto* compositionState = editCompositionState();
+
+    // 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 = getCroppedBufferSize(drawingState);
+    // Subtract the transparent region and snap to the bounds
+    Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
+    Rect frame(getTransform().transform(bounds));
+
+    compositionState->cursorFrame = frame;
+}
+
+sp<compositionengine::LayerFE> Layer::asLayerFE() const {
+    return const_cast<compositionengine::LayerFE*>(
+            static_cast<const compositionengine::LayerFE*>(this));
+}
+
+sp<compositionengine::LayerFE> Layer::getCompositionEngineLayerFE() const {
+    return nullptr;
+}
+
+compositionengine::LayerFECompositionState* Layer::editCompositionState() {
+    return nullptr;
+}
+
+const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
+    return nullptr;
+}
+
+bool Layer::onPreComposition(nsecs_t) {
+    return false;
+}
+
+void Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {
+    using StateSubset = compositionengine::LayerFE::StateSubset;
+
+    switch (subset) {
+        case StateSubset::BasicGeometry:
+            prepareBasicGeometryCompositionState();
+            break;
+
+        case StateSubset::GeometryAndContent:
+            prepareBasicGeometryCompositionState();
+            prepareGeometryCompositionState();
+            preparePerFrameCompositionState();
+            break;
+
+        case StateSubset::Content:
+            preparePerFrameCompositionState();
+            break;
+
+        case StateSubset::Cursor:
+            prepareCursorCompositionState();
+            break;
     }
 }
 
 const char* Layer::getDebugName() const {
-    return mName.string();
-}
-
-void Layer::forceClientComposition(const sp<DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    outputLayer->editState().forceClientComposition = true;
-}
-
-bool Layer::getForceClientComposition(const sp<DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    return outputLayer->getState().forceClientComposition;
-}
-
-void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-
-    if (!outputLayer->getState().hwc ||
-        (*outputLayer->getState().hwc).hwcCompositionType !=
-                Hwc2::IComposerClient::Composition::CURSOR) {
-        return;
-    }
-
-    // This gives us only the "orientation" component of the transform
-    const State& s(getDrawingState());
-
-    // 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 = getCroppedBufferSize(s);
-    // Subtract the transparent region and snap to the bounds
-    Rect bounds = reduce(win, getActiveTransparentRegion(s));
-    Rect frame(getTransform().transform(bounds));
-    frame.intersect(display->getViewport(), &frame);
-    auto& displayTransform = display->getTransform();
-    auto position = displayTransform.transform(frame);
-
-    auto error =
-            (*outputLayer->getState().hwc).hwcLayer->setCursorPosition(position.left, position.top);
-    ALOGE_IF(error != HWC2::Error::None,
-             "[%s] Failed to set cursor position "
-             "to (%d, %d): %s (%d)",
-             mName.string(), position.left, position.top, to_string(error).c_str(),
-             static_cast<int32_t>(error));
+    return mName.c_str();
 }
 
 // ---------------------------------------------------------------------------
 // drawing...
 // ---------------------------------------------------------------------------
 
-bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                               Region& clearRegion, const bool supportProtectedContent,
-                               renderengine::LayerSettings& layer) {
-    return prepareClientLayer(renderArea, clip, false, clearRegion, supportProtectedContent, layer);
-}
+std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+    if (!getCompositionState()) {
+        return {};
+    }
 
-bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
-                               Region& clearRegion, const bool supportProtectedContent,
-                               renderengine::LayerSettings& layer) {
-    return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform,
-                              clearRegion, supportProtectedContent, layer);
-}
-
-bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/,
-                               bool useIdentityTransform, Region& /*clearRegion*/,
-                               const bool /*supportProtectedContent*/,
-                               renderengine::LayerSettings& layer) {
     FloatRect bounds = getBounds();
     half alpha = getAlpha();
-    layer.geometry.boundaries = bounds;
-    if (useIdentityTransform) {
-        layer.geometry.positionTransform = mat4();
+
+    compositionengine::LayerFE::LayerSettings layerSettings;
+    layerSettings.geometry.boundaries = bounds;
+    if (targetSettings.useIdentityTransform) {
+        layerSettings.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;
+        layerSettings.geometry.positionTransform = getTransform().asMatrix4();
     }
 
     if (hasColorTransform()) {
-        layer.colorTransform = getColorTransform();
+        layerSettings.colorTransform = getColorTransform();
     }
 
     const auto roundedCornerState = getRoundedCornerState();
-    layer.geometry.roundedCornersRadius = roundedCornerState.radius;
-    layer.geometry.roundedCornersCrop = roundedCornerState.cropRect;
+    layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius;
+    layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect;
 
-    layer.alpha = alpha;
-    layer.sourceDataspace = mCurrentDataSpace;
-    return true;
+    layerSettings.alpha = alpha;
+    layerSettings.sourceDataspace = getDataSpace();
+    layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
+    return layerSettings;
 }
 
-void Layer::setCompositionType(const sp<const DisplayDevice>& display,
-                               Hwc2::IComposerClient::Composition type) {
-    const auto outputLayer = findOutputLayerForDisplay(display);
-    LOG_FATAL_IF(!outputLayer);
-    LOG_FATAL_IF(!outputLayer->getState().hwc);
-    auto& compositionState = outputLayer->editState();
-
-    ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", ((*compositionState.hwc).hwcLayer)->getId(),
-          toString(type).c_str(), 1);
-    if ((*compositionState.hwc).hwcCompositionType != type) {
-        ALOGV("    actually setting");
-        (*compositionState.hwc).hwcCompositionType = type;
-
-        auto error = (*compositionState.hwc)
-                             .hwcLayer->setCompositionType(static_cast<HWC2::Composition>(type));
-        ALOGE_IF(error != HWC2::Error::None,
-                 "[%s] Failed to set "
-                 "composition type %s: %s (%d)",
-                 mName.string(), toString(type).c_str(), to_string(error).c_str(),
-                 static_cast<int32_t>(error));
+std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition(
+        const LayerFE::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+        ui::Dataspace outputDataspace) {
+    renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+    if (shadow.length <= 0.f) {
+        return {};
     }
+
+    const float casterAlpha = casterLayerSettings.alpha;
+    const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) &&
+                                 casterLayerSettings.source.buffer.isOpaque);
+
+    compositionengine::LayerFE::LayerSettings shadowLayer = casterLayerSettings;
+
+    shadowLayer.shadow = shadow;
+    shadowLayer.geometry.boundaries = mBounds; // ignore transparent region
+
+    // If the casting layer is translucent, we need to fill in the shadow underneath the layer.
+    // Otherwise the generated shadow will only be shown around the casting layer.
+    shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+    shadowLayer.shadow.ambientColor *= casterAlpha;
+    shadowLayer.shadow.spotColor *= casterAlpha;
+    shadowLayer.sourceDataspace = outputDataspace;
+    shadowLayer.source.buffer.buffer = nullptr;
+    shadowLayer.source.buffer.fence = nullptr;
+    shadowLayer.frameNumber = 0;
+    shadowLayer.bufferId = 0;
+
+    if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
+        return {};
+    }
+
+    float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
+    const FloatRect& cornerRadiusCropRect = shadowLayer.geometry.roundedCornersCrop;
+    const FloatRect& casterRect = shadowLayer.geometry.boundaries;
+
+    // crop used to set the corner radius may be larger than the content rect. Adjust the corner
+    // radius accordingly.
+    if (casterCornerRadius > 0.f) {
+        float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
+                                        std::abs(cornerRadiusCropRect.left - casterRect.left));
+        if (cropRectOffset > casterCornerRadius) {
+            casterCornerRadius = 0;
+        } else {
+            casterCornerRadius -= cropRectOffset;
+        }
+        shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
+    }
+
+    return shadowLayer;
+}
+
+void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings,
+                                          bool blackout) const {
+    layerSettings.source.buffer.buffer = nullptr;
+    layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
+    layerSettings.disableBlending = true;
+    layerSettings.frameNumber = 0;
+
+    // If layer is blacked out, force alpha to 1 so that we draw a black color layer.
+    layerSettings.alpha = blackout ? 1.0f : 0.0f;
+}
+
+std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList(
+        compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) {
+    std::optional<compositionengine::LayerFE::LayerSettings> layerSettings =
+            prepareClientComposition(targetSettings);
+    // Nothing to render.
+    if (!layerSettings) {
+        return {};
+    }
+
+    // HWC requests to clear this layer.
+    if (targetSettings.clearContent) {
+        prepareClearClientComposition(*layerSettings, false /* blackout */);
+        return {*layerSettings};
+    }
+
+    std::optional<compositionengine::LayerFE::LayerSettings> shadowSettings =
+            prepareShadowClientComposition(*layerSettings, targetSettings.viewport,
+                                           targetSettings.dataspace);
+    // There are no shadows to render.
+    if (!shadowSettings) {
+        return {*layerSettings};
+    }
+
+    // If the layer casts a shadow but the content casting the shadow is occluded, skip
+    // composing the non-shadow content and only draw the shadows.
+    if (targetSettings.realContentIsVisible) {
+        return {*shadowSettings, *layerSettings};
+    }
+
+    return {*shadowSettings};
 }
 
 Hwc2::IComposerClient::Composition Layer::getCompositionType(
@@ -618,58 +747,11 @@
 // local state
 // ----------------------------------------------------------------------------
 
-void Layer::computeGeometry(const RenderArea& renderArea,
-                            renderengine::Mesh& mesh,
-                            bool useIdentityTransform) const {
-    const ui::Transform renderAreaTransform(renderArea.getTransform());
-    FloatRect win = getBounds();
-
-    vec2 lt = vec2(win.left, win.top);
-    vec2 lb = vec2(win.left, win.bottom);
-    vec2 rb = vec2(win.right, win.bottom);
-    vec2 rt = vec2(win.right, win.top);
-
-    ui::Transform layerTransform = getTransform();
-    if (!useIdentityTransform) {
-        lt = layerTransform.transform(lt);
-        lb = layerTransform.transform(lb);
-        rb = layerTransform.transform(rb);
-        rt = layerTransform.transform(rt);
-    }
-
-    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);
-}
-
 bool Layer::isSecure() const {
     const State& s(mDrawingState);
     return (s.flags & layer_state_t::eLayerSecure);
 }
 
-void Layer::setVisibleRegion(const Region& visibleRegion) {
-    // always called from main thread
-    this->visibleRegion = visibleRegion;
-}
-
-void Layer::setCoveredRegion(const Region& coveredRegion) {
-    // always called from main thread
-    this->coveredRegion = coveredRegion;
-}
-
-void Layer::setVisibleNonTransparentRegion(const Region& setVisibleNonTransparentRegion) {
-    // always called from main thread
-    this->visibleNonTransparentRegion = setVisibleNonTransparentRegion;
-}
-
-void Layer::clearVisibilityRegions() {
-    visibleRegion.clear();
-    visibleNonTransparentRegion.clear();
-    coveredRegion.clear();
-}
-
 // ----------------------------------------------------------------------------
 // transaction
 // ----------------------------------------------------------------------------
@@ -687,7 +769,7 @@
     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());
+            ALOGE("[%s] Unable to promote barrier Layer.", getDebugName());
             // 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).
@@ -711,7 +793,7 @@
         mFlinger->setTransactionFlags(eTraversalNeeded);
     }
     mPendingStates.push_back(mCurrentState);
-    ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
 
 void Layer::popPendingState(State* stateToCommit) {
@@ -719,7 +801,7 @@
     *stateToCommit = mPendingStates[0];
 
     mPendingStates.removeAt(0);
-    ATRACE_INT(mTransactionName.string(), mPendingStates.size());
+    ATRACE_INT(mTransactionName.c_str(), mPendingStates.size());
 }
 
 bool Layer::applyPendingStates(State* stateToCommit) {
@@ -730,7 +812,7 @@
                 // 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
                 // into too much trouble.
-                ALOGE("[%s] No local sync point found", mName.string());
+                ALOGE("[%s] No local sync point found", getDebugName());
                 popPendingState(stateToCommit);
                 stateUpdateAvailable = true;
                 continue;
@@ -738,7 +820,7 @@
 
             if (mRemoteSyncPoints.front()->getFrameNumber() !=
                 mPendingStates[0].frameNumber_legacy) {
-                ALOGE("[%s] Unexpected sync point frame number found", mName.string());
+                ALOGE("[%s] Unexpected sync point frame number found", getDebugName());
 
                 // Signal our end of the sync point and then dispose of it
                 mRemoteSyncPoints.front()->setTransactionApplied();
@@ -790,7 +872,7 @@
                  "            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(),
+                 this, getName().c_str(), getBufferTransform(), 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,
@@ -822,7 +904,7 @@
     const bool resizePending =
             ((stateToCommit->requested_legacy.w != stateToCommit->active_legacy.w) ||
              (stateToCommit->requested_legacy.h != stateToCommit->active_legacy.h)) &&
-            (mActiveBuffer != nullptr);
+            (getBuffer() != nullptr);
     if (!isFixedSize()) {
         if (resizePending && mSidebandStream == nullptr) {
             flags |= eDontUpdateGeometryState;
@@ -835,11 +917,6 @@
     if (!(flags & eDontUpdateGeometryState)) {
         State& editCurrentState(getCurrentState());
 
-        // If mFreezeGeometryUpdates is true we are in the setGeometryAppliesWithResize
-        // mode, which causes attributes which normally latch regardless of scaling mode,
-        // to be delayed. We copy the requested state to the active state making sure
-        // to respect these rules (again see Layer.h for a detailed discussion).
-        //
         // There is an awkward asymmetry in the handling of the crop states in the position
         // states, as can be seen below. Largely this arises from position and transform
         // being stored in the same data structure while having different latching rules.
@@ -847,16 +924,8 @@
         //
         // Careful that "stateToCommit" and editCurrentState may not begin as equivalent due to
         // applyPendingStates in the presence of deferred transactions.
-        if (mFreezeGeometryUpdates) {
-            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_legacy = editCurrentState.requested_legacy;
-            stateToCommit->active_legacy = stateToCommit->requested_legacy;
-        }
+        editCurrentState.active_legacy = editCurrentState.requested_legacy;
+        stateToCommit->active_legacy = stateToCommit->requested_legacy;
     }
 
     return flags;
@@ -866,6 +935,15 @@
     ATRACE_CALL();
 
     if (mLayerDetached) {
+        // Ensure BLAST buffer callbacks are processed.
+        // detachChildren and mLayerDetached were implemented to avoid geometry updates
+        // to layers in the cases of animation. For BufferQueue layers buffers are still
+        // consumed as normal. This is useful as otherwise the client could get hung
+        // inevitably waiting on a buffer to return. We recreate this semantic for BufferQueue
+        // even though it is a little consistent. detachChildren is shortly slated for removal
+        // by the hierarchy mirroring work so we don't need to worry about it too much.
+        forceSendCallbacks();
+        mCurrentState.callbackHandles = {};
         return flags;
     }
 
@@ -923,7 +1001,7 @@
     return mTransactionFlags.fetch_or(flags);
 }
 
-bool Layer::setPosition(float x, float y, bool immediate) {
+bool Layer::setPosition(float x, float y) {
     if (mCurrentState.requested_legacy.transform.tx() == x &&
         mCurrentState.requested_legacy.transform.ty() == y)
         return false;
@@ -933,14 +1011,11 @@
     // 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_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_legacy.transform.set(x, y);
-    }
-    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
+    // 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_legacy.transform.set(x, y);
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1010,6 +1085,8 @@
     mCurrentState.zOrderRelativeOf = relativeOf;
     mCurrentState.sequence++;
     mCurrentState.modified = true;
+    mCurrentState.isRelativeOf = relativeOf != nullptr;
+
     setTransactionFlags(eTransactionNeeded);
 }
 
@@ -1076,10 +1153,11 @@
 
     if (!mCurrentState.bgColorLayer && alpha != 0) {
         // create background color layer if one does not yet exist
-        uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor;
-        const String8& name = mName + "BackgroundColorLayer";
-        mCurrentState.bgColorLayer = new ColorLayer(
-                LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata()));
+        uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect;
+        std::string name = mName + "BackgroundColorLayer";
+        mCurrentState.bgColorLayer = mFlinger->getFactory().createEffectLayer(
+                LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), 0, 0, flags,
+                                  LayerMetadata()));
 
         // add to child list
         addChild(mCurrentState.bgColorLayer);
@@ -1113,6 +1191,16 @@
     return true;
 }
 
+bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) {
+    if (mCurrentState.backgroundBlurRadius == backgroundBlurRadius) return false;
+
+    mCurrentState.sequence++;
+    mCurrentState.backgroundBlurRadius = backgroundBlurRadius;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
 bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
         bool allowNonRectPreservingTransforms) {
     ui::Transform t;
@@ -1136,6 +1224,7 @@
     setTransactionFlags(eTransactionNeeded);
     return true;
 }
+
 bool Layer::setFlags(uint8_t flags, uint8_t mask) {
     const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask);
     if (mCurrentState.flags == newFlags) return false;
@@ -1146,14 +1235,11 @@
     return true;
 }
 
-bool Layer::setCrop_legacy(const Rect& crop, bool immediate) {
+bool Layer::setCrop_legacy(const Rect& crop) {
     if (mCurrentState.requestedCrop_legacy == crop) return false;
     mCurrentState.sequence++;
     mCurrentState.requestedCrop_legacy = crop;
-    if (immediate && !mFreezeGeometryUpdates) {
-        mCurrentState.crop_legacy = crop;
-    }
-    mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate;
+    mCurrentState.crop_legacy = crop;
 
     mCurrentState.modified = true;
     setTransactionFlags(eTransactionNeeded);
@@ -1195,6 +1281,29 @@
     return true;
 }
 
+bool Layer::setFrameRateSelectionPriority(int32_t priority) {
+    if (mCurrentState.frameRateSelectionPriority == priority) return false;
+    mCurrentState.frameRateSelectionPriority = priority;
+    mCurrentState.sequence++;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+int32_t Layer::getFrameRateSelectionPriority() const {
+    // Check if layer has priority set.
+    if (mDrawingState.frameRateSelectionPriority != PRIORITY_UNSET) {
+        return mDrawingState.frameRateSelectionPriority;
+    }
+    // If not, search whether its parents have it set.
+    sp<Layer> parent = getParent();
+    if (parent != nullptr) {
+        return parent->getFrameRateSelectionPriority();
+    }
+
+    return Layer::PRIORITY_UNSET;
+}
+
 uint32_t Layer::getLayerStack() const {
     auto p = mDrawingParent.promote();
     if (p == nullptr) {
@@ -1203,6 +1312,37 @@
     return p->getLayerStack();
 }
 
+bool Layer::setShadowRadius(float shadowRadius) {
+    if (mCurrentState.shadowRadius == shadowRadius) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.shadowRadius = shadowRadius;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+bool Layer::setFrameRate(FrameRate frameRate) {
+    if (!mFlinger->useFrameRateApi) {
+        return false;
+    }
+    if (mCurrentState.frameRate == frameRate) {
+        return false;
+    }
+
+    mCurrentState.sequence++;
+    mCurrentState.frameRate = frameRate;
+    mCurrentState.modified = true;
+    setTransactionFlags(eTransactionNeeded);
+    return true;
+}
+
+Layer::FrameRate Layer::getFrameRate() const {
+    return getDrawingState().frameRate;
+}
+
 void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
     ATRACE_CALL();
     mCurrentState.barrierLayer_legacy = barrierLayer;
@@ -1277,14 +1417,17 @@
 
 // TODO(marissaw): add new layer state info to layer debugging
 LayerDebugInfo Layer::getLayerDebugInfo() const {
+    using namespace std::string_literals;
+
     LayerDebugInfo info;
     const State& ds = getDrawingState();
     info.mName = getName();
     sp<Layer> parent = getParent();
-    info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string());
-    info.mType = std::string(getTypeId());
+    info.mParentName = parent ? parent->getName() : "none"s;
+    info.mType = getType();
     info.mTransparentRegion = ds.activeTransparentRegion_legacy;
-    info.mVisibleRegion = visibleRegion;
+
+    info.mVisibleRegion = debugGetVisibleRegionOnDefaultDisplay();
     info.mSurfaceDamageRegion = surfaceDamageRegion;
     info.mLayerStack = getLayerStack();
     info.mX = ds.active_legacy.transform.tx();
@@ -1296,13 +1439,13 @@
     info.mColor = ds.color;
     info.mFlags = ds.flags;
     info.mPixelFormat = getPixelFormat();
-    info.mDataSpace = static_cast<android_dataspace>(mCurrentDataSpace);
+    info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
     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 = mActiveBuffer;
+        sp<const GraphicBuffer> buffer = getBuffer();
         if (buffer != 0) {
             info.mActiveBufferWidth = buffer->getWidth();
             info.mActiveBufferHeight = buffer->getHeight();
@@ -1347,18 +1490,18 @@
     std::string name;
     if (mName.length() > 77) {
         std::string shortened;
-        shortened.append(mName.string(), 36);
+        shortened.append(mName, 0, 36);
         shortened.append("[...]");
-        shortened.append(mName.string() + (mName.length() - 36), 36);
-        name = shortened;
+        shortened.append(mName, mName.length() - 36);
+        name = std::move(shortened);
     } else {
-        name = std::string(mName.string(), mName.size());
+        name = mName;
     }
 
     StringAppendF(&result, " %s\n", name.c_str());
 
     const State& layerState(getDrawingState());
-    const auto& compositionState = outputLayer->getState();
+    const auto& outputLayerState = outputLayer->getState();
 
     if (layerState.zOrderRelativeOf != nullptr || mDrawingParent != nullptr) {
         StringAppendF(&result, "  rel %6d | ", layerState.z);
@@ -1367,13 +1510,10 @@
     }
     StringAppendF(&result, "  %10d | ", mWindowType);
     StringAppendF(&result, "%10s | ", toString(getCompositionType(displayDevice)).c_str());
-    StringAppendF(&result, "%10s | ",
-                  toString(getCompositionLayer() ? compositionState.bufferTransform
-                                                 : static_cast<Hwc2::Transform>(0))
-                          .c_str());
-    const Rect& frame = compositionState.displayFrame;
+    StringAppendF(&result, "%10s | ", toString(outputLayerState.bufferTransform).c_str());
+    const Rect& frame = outputLayerState.displayFrame;
     StringAppendF(&result, "%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom);
-    const FloatRect& crop = compositionState.sourceCrop;
+    const FloatRect& crop = outputLayerState.sourceCrop;
     StringAppendF(&result, "%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right,
                   crop.bottom);
 
@@ -1399,16 +1539,23 @@
 }
 
 void Layer::dumpFrameEvents(std::string& result) {
-    StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().string(), getTypeId(), this);
+    StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().c_str(), getType(), this);
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.checkFencesForCompletion();
     mFrameEventHistory.dump(result);
 }
 
+void Layer::dumpCallingUidPid(std::string& result) const {
+    StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().c_str(), getType(),
+                  mCallingPid, mCallingUid);
+}
+
 void Layer::onDisconnect() {
     Mutex::Autolock lock(mFrameEventHistoryMutex);
     mFrameEventHistory.onDisconnect();
-    mFlinger->mTimeStats->onDestroy(getSequence());
+    const int32_t layerId = getSequence();
+    mFlinger->mTimeStats->onDestroy(layerId);
+    mFlinger->mFrameTracer->onDestroy(layerId);
 }
 
 void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
@@ -1416,6 +1563,8 @@
     if (newTimestamps) {
         mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber,
                                           getName().c_str(), newTimestamps->postedTime);
+        mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber,
+                                              newTimestamps->acquireFence);
     }
 
     Mutex::Autolock lock(mFrameEventHistoryMutex);
@@ -1487,8 +1636,8 @@
     for (const sp<Layer>& child : mDrawingChildren) {
         child->mDrawingParent = newParent;
         child->computeBounds(newParent->mBounds,
-                             newParent->getTransformWithScale(
-                                     newParent->getBufferScaleTransform()));
+                             newParent->getTransformWithScale(newParent->getBufferScaleTransform()),
+                             newParent->mEffectiveShadowRadius);
     }
 }
 
@@ -1608,22 +1757,25 @@
 
 bool Layer::isLegacyDataSpace() const {
     // return true when no higher bits are set
-    return !(mCurrentDataSpace & (ui::Dataspace::STANDARD_MASK |
-                ui::Dataspace::TRANSFER_MASK | ui::Dataspace::RANGE_MASK));
+    return !(getDataSpace() &
+             (ui::Dataspace::STANDARD_MASK | ui::Dataspace::TRANSFER_MASK |
+              ui::Dataspace::RANGE_MASK));
 }
 
 void Layer::setParent(const sp<Layer>& layer) {
     mCurrentParent = layer;
 }
 
-int32_t Layer::getZ() const {
-    return mDrawingState.z;
+int32_t Layer::getZ(LayerVector::StateSet stateSet) const {
+    const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+    const State& state = useDrawing ? mDrawingState : mCurrentState;
+    return state.z;
 }
 
 bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
     const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
     const State& state = useDrawing ? mDrawingState : mCurrentState;
-    return state.zOrderRelativeOf != nullptr;
+    return state.isRelativeOf;
 }
 
 __attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList(
@@ -1648,8 +1800,7 @@
     }
 
     for (const sp<Layer>& child : children) {
-        const State& childState = useDrawing ? child->mDrawingState : child->mCurrentState;
-        if (childState.zOrderRelativeOf != nullptr) {
+        if (child->usingRelativeZ(stateSet)) {
             continue;
         }
         traverse.add(child);
@@ -1678,7 +1829,7 @@
             continue;
         }
 
-        if (relative->getZ() >= 0) {
+        if (relative->getZ(stateSet) >= 0) {
             break;
         }
         relative->traverseInZOrder(stateSet, visitor);
@@ -1712,7 +1863,7 @@
             continue;
         }
 
-        if (relative->getZ() < 0) {
+        if (relative->getZ(stateSet) < 0) {
             break;
         }
         relative->traverseInReverseZOrder(stateSet, visitor);
@@ -1729,6 +1880,15 @@
     }
 }
 
+void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) {
+    visitor(this);
+    const LayerVector& children =
+            state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren;
+    for (const sp<Layer>& child : children) {
+        child->traverse(state, visitor);
+    }
+}
+
 LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                              const std::vector<Layer*>& layersInTree) {
     LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
@@ -1770,7 +1930,7 @@
     size_t i = 0;
     for (; i < list.size(); i++) {
         const auto& relative = list[i];
-        if (relative->getZ() >= 0) {
+        if (relative->getZ(stateSet) >= 0) {
             break;
         }
         relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor);
@@ -1820,6 +1980,10 @@
     return half4(color.r, color.g, color.b, getAlpha());
 }
 
+int32_t Layer::getBackgroundBlurRadius() const {
+    return getDrawingState().backgroundBlurRadius;
+}
+
 Layer::RoundedCornerState Layer::getRoundedCornerState() const {
     const auto& p = mDrawingParent.promote();
     if (p != nullptr) {
@@ -1842,6 +2006,18 @@
             : RoundedCornerState();
 }
 
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+    renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
+
+    // Shift the spot light x-position to the middle of the display and then
+    // offset it by casting layer's screen pos.
+    state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+    state.lightPos.y -= mScreenBounds.top;
+
+    state.length = mEffectiveShadowRadius;
+    return state;
+}
+
 void Layer::commitChildList() {
     for (size_t i = 0; i < mCurrentChildren.size(); i++) {
         const auto& child = mCurrentChildren[i];
@@ -1874,6 +2050,18 @@
     setTransactionFlags(eTransactionNeeded);
 }
 
+LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) const {
+    LayerProto* layerProto = layersProto.add_layers();
+    writeToProtoDrawingState(layerProto, traceFlags);
+    writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
+
+    for (const sp<Layer>& layer : mDrawingChildren) {
+        layer->writeToProto(layersProto, traceFlags);
+    }
+
+    return layerProto;
+}
+
 void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) const {
     ui::Transform transform = getTransform();
 
@@ -1887,17 +2075,16 @@
             }
         }
 
-        auto buffer = mActiveBuffer;
+        auto buffer = getBuffer();
         if (buffer != nullptr) {
             LayerProtoHelper::writeToProto(buffer,
                                            [&]() { return layerInfo->mutable_active_buffer(); });
-            LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform),
+            LayerProtoHelper::writeToProto(ui::Transform(getBufferTransform()),
                                            layerInfo->mutable_buffer_transform());
         }
         layerInfo->set_invalidate(contentDirty);
         layerInfo->set_is_protected(isProtected());
-        layerInfo->set_dataspace(
-                dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace)));
+        layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
         layerInfo->set_queued_frames(getQueuedFrameCount());
         layerInfo->set_refresh_pending(isBufferLatched());
         layerInfo->set_curr_frame(mCurrentFrameNumber);
@@ -1908,10 +2095,15 @@
         LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
                                                [&]() { return layerInfo->mutable_position(); });
         LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
-        LayerProtoHelper::writeToProto(visibleRegion,
+        LayerProtoHelper::writeToProto(debugGetVisibleRegionOnDefaultDisplay(),
                                        [&]() { return layerInfo->mutable_visible_region(); });
         LayerProtoHelper::writeToProto(surfaceDamageRegion,
                                        [&]() { return layerInfo->mutable_damage_region(); });
+
+        if (hasColorTransform()) {
+            LayerProtoHelper::writeToProto(getColorTransform(),
+                                           layerInfo->mutable_color_transform());
+        }
     }
 
     if (traceFlags & SurfaceTracing::TRACE_EXTRA) {
@@ -1919,6 +2111,9 @@
                                        [&]() { return layerInfo->mutable_source_bounds(); });
         LayerProtoHelper::writeToProto(mScreenBounds,
                                        [&]() { return layerInfo->mutable_screen_bounds(); });
+        LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect,
+                                       [&]() { return layerInfo->mutable_corner_radius_crop(); });
+        layerInfo->set_shadow_radius(mEffectiveShadowRadius);
     }
 }
 
@@ -1933,7 +2128,7 @@
     if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
         layerInfo->set_id(sequence);
         layerInfo->set_name(getName().c_str());
-        layerInfo->set_type(String8(getTypeId()));
+        layerInfo->set_type(getType());
 
         for (const auto& child : children) {
             layerInfo->add_children(child->sequence);
@@ -1988,6 +2183,8 @@
         } else {
             layerInfo->set_z_order_relative_of(-1);
         }
+
+        layerInfo->set_is_relative_of(state.isRelativeOf);
     }
 
     if (traceFlags & SurfaceTracing::TRACE_INPUT) {
@@ -2003,41 +2200,13 @@
     }
 }
 
-void Layer::writeToProtoCompositionState(LayerProto* layerInfo,
-                                         const sp<DisplayDevice>& displayDevice,
-                                         uint32_t traceFlags) const {
-    auto outputLayer = findOutputLayerForDisplay(displayDevice);
-    if (!outputLayer) {
-        return;
-    }
-
-    writeToProtoDrawingState(layerInfo, traceFlags);
-    writeToProtoCommonState(layerInfo, LayerVector::StateSet::Drawing, traceFlags);
-
-    const auto& compositionState = outputLayer->getState();
-
-    const Rect& frame = compositionState.displayFrame;
-    LayerProtoHelper::writeToProto(frame, [&]() { return layerInfo->mutable_hwc_frame(); });
-
-    const FloatRect& crop = compositionState.sourceCrop;
-    LayerProtoHelper::writeToProto(crop, [&]() { return layerInfo->mutable_hwc_crop(); });
-
-    const int32_t transform =
-            getCompositionLayer() ? static_cast<int32_t>(compositionState.bufferTransform) : 0;
-    layerInfo->set_hwc_transform(transform);
-
-    const int32_t compositionType =
-            static_cast<int32_t>(compositionState.hwc ? (*compositionState.hwc).hwcCompositionType
-                                                      : Hwc2::IComposerClient::Composition::CLIENT);
-    layerInfo->set_hwc_composition_type(compositionType);
-}
-
 bool Layer::isRemovedFromCurrentState() const  {
     return mRemovedFromCurrentState;
 }
 
 InputWindowInfo Layer::fillInputInfo() {
     InputWindowInfo info = mDrawingState.inputInfo;
+    info.id = sequence;
 
     if (info.displayId == ADISPLAY_ID_NONE) {
         info.displayId = getLayerStack();
@@ -2094,24 +2263,187 @@
         info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
     }
 
+    // If the layer is a clone, we need to crop the input region to cloned root to prevent
+    // touches from going outside the cloned area.
+    if (isClone()) {
+        sp<Layer> clonedRoot = getClonedRoot();
+        if (clonedRoot != nullptr) {
+            Rect rect(clonedRoot->mScreenBounds);
+            info.touchableRegion = info.touchableRegion.intersect(rect);
+        }
+    }
+
     return info;
 }
 
+sp<Layer> Layer::getClonedRoot() {
+    if (mClonedChild != nullptr) {
+        return this;
+    }
+    if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
+        return nullptr;
+    }
+    return mDrawingParent.promote()->getClonedRoot();
+}
+
 bool Layer::hasInput() const {
     return mDrawingState.inputInfo.token != nullptr;
 }
 
-std::shared_ptr<compositionengine::Layer> Layer::getCompositionLayer() const {
-    return nullptr;
-}
-
 bool Layer::canReceiveInput() const {
     return !isHiddenByPolicy();
 }
 
 compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
         const sp<const DisplayDevice>& display) const {
-    return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionLayer().get());
+    return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE());
+}
+
+Region Layer::debugGetVisibleRegionOnDefaultDisplay() const {
+    sp<DisplayDevice> displayDevice = mFlinger->getDefaultDisplayDeviceLocked();
+    if (displayDevice == nullptr) {
+        return {};
+    }
+
+    auto outputLayer = findOutputLayerForDisplay(displayDevice);
+    if (outputLayer == nullptr) {
+        return {};
+    }
+
+    return outputLayer->getState().visibleRegion;
+}
+
+void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
+    // copy drawing state from cloned layer
+    mDrawingState = clonedFrom->mDrawingState;
+    mClonedFrom = clonedFrom;
+}
+
+void Layer::updateMirrorInfo() {
+    if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) {
+        // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false,
+        // it means that there is a clone, but the layer it was cloned from has been destroyed. In
+        // that case, we want to delete the reference to the clone since we want it to get
+        // destroyed. The root, this layer, will still be around since the client can continue
+        // to hold a reference, but no cloned layers will be displayed.
+        mClonedChild = nullptr;
+        return;
+    }
+
+    std::map<sp<Layer>, sp<Layer>> clonedLayersMap;
+    // If the real layer exists and is in current state, add the clone as a child of the root.
+    // There's no need to remove from drawingState when the layer is offscreen since currentState is
+    // copied to drawingState for the root layer. So the clonedChild is always removed from
+    // drawingState and then needs to be added back each traversal.
+    if (!mClonedChild->getClonedFrom()->isRemovedFromCurrentState()) {
+        addChildToDrawing(mClonedChild);
+    }
+
+    mClonedChild->updateClonedDrawingState(clonedLayersMap);
+    mClonedChild->updateClonedChildren(this, clonedLayersMap);
+    mClonedChild->updateClonedRelatives(clonedLayersMap);
+}
+
+void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    // If the layer the clone was cloned from is alive, copy the content of the drawingState
+    // to the clone. If the real layer is no longer alive, continue traversing the children
+    // since we may be able to pull out other children that are still alive.
+    if (isClonedFromAlive()) {
+        sp<Layer> clonedFrom = getClonedFrom();
+        mDrawingState = clonedFrom->mDrawingState;
+        clonedLayersMap.emplace(clonedFrom, this);
+    }
+
+    // The clone layer may have children in drawingState since they may have been created and
+    // added from a previous request to updateMirorInfo. This is to ensure we don't recreate clones
+    // that already exist, since we can just re-use them.
+    // The drawingChildren will not get overwritten by the currentChildren since the clones are
+    // not updated in the regular traversal. They are skipped since the root will lose the
+    // reference to them when it copies its currentChildren to drawing.
+    for (sp<Layer>& child : mDrawingChildren) {
+        child->updateClonedDrawingState(clonedLayersMap);
+    }
+}
+
+void Layer::updateClonedChildren(const sp<Layer>& mirrorRoot,
+                                 std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    mDrawingChildren.clear();
+
+    if (!isClonedFromAlive()) {
+        return;
+    }
+
+    sp<Layer> clonedFrom = getClonedFrom();
+    for (sp<Layer>& child : clonedFrom->mDrawingChildren) {
+        if (child == mirrorRoot) {
+            // This is to avoid cyclical mirroring.
+            continue;
+        }
+        sp<Layer> clonedChild = clonedLayersMap[child];
+        if (clonedChild == nullptr) {
+            clonedChild = child->createClone();
+            clonedLayersMap[child] = clonedChild;
+        }
+        addChildToDrawing(clonedChild);
+        clonedChild->updateClonedChildren(mirrorRoot, clonedLayersMap);
+    }
+}
+
+void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+    if (cropLayer != nullptr) {
+        if (clonedLayersMap.count(cropLayer) == 0) {
+            // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to
+            // self as crop layer to avoid going outside bounds.
+            mDrawingState.touchableRegionCrop = this;
+        } else {
+            const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer);
+            mDrawingState.touchableRegionCrop = clonedCropLayer;
+        }
+    }
+    // Cloned layers shouldn't handle watch outside since their z order is not determined by
+    // WM or the client.
+    mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+}
+
+void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+    mDrawingState.zOrderRelativeOf = nullptr;
+    mDrawingState.zOrderRelatives.clear();
+
+    if (!isClonedFromAlive()) {
+        return;
+    }
+
+    const sp<Layer>& clonedFrom = getClonedFrom();
+    for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
+        const sp<Layer>& relative = relativeWeak.promote();
+        if (clonedLayersMap.count(relative) > 0) {
+            auto& clonedRelative = clonedLayersMap.at(relative);
+            mDrawingState.zOrderRelatives.add(clonedRelative);
+        }
+    }
+
+    // Check if the relativeLayer for the real layer is part of the cloned hierarchy.
+    // It's possible that the layer it's relative to is outside the requested cloned hierarchy.
+    // In that case, we treat the layer as if the relativeOf has been removed. This way, it will
+    // still traverse the children, but the layer with the missing relativeOf will not be shown
+    // on screen.
+    const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
+    if (clonedLayersMap.count(relativeOf) > 0) {
+        const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf);
+        mDrawingState.zOrderRelativeOf = clonedRelativeOf;
+    }
+
+    updateClonedInputInfo(clonedLayersMap);
+
+    for (sp<Layer>& child : mDrawingChildren) {
+        child->updateClonedRelatives(clonedLayersMap);
+    }
+}
+
+void Layer::addChildToDrawing(const sp<Layer>& layer) {
+    mDrawingChildren.add(layer);
+    layer->mDrawingParent = this;
 }
 
 // ---------------------------------------------------------------------------
@@ -2125,3 +2457,6 @@
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
 #endif
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 3b4d873..37ae340 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -34,7 +34,6 @@
 #include <ui/Region.h>
 #include <ui/Transform.h>
 #include <utils/RefBase.h>
-#include <utils/String8.h>
 #include <utils/Timers.h>
 
 #include <cstdint>
@@ -67,7 +66,6 @@
 class LayerDebugInfo;
 
 namespace compositionengine {
-class Layer;
 class OutputLayer;
 struct LayerFECompositionState;
 }
@@ -79,29 +77,28 @@
 // ---------------------------------------------------------------------------
 
 struct LayerCreationArgs {
-    LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name,
-                      uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
-          : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags),
-            metadata(std::move(metadata)) {}
+    LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client> client, std::string name,
+                      uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata);
 
     SurfaceFlinger* flinger;
-    const sp<Client>& client;
-    const String8& name;
+    const sp<Client> client;
+    std::string name;
     uint32_t w;
     uint32_t h;
     uint32_t flags;
     LayerMetadata metadata;
+    pid_t callingPid;
+    uid_t callingUid;
+    sp<const DisplayDevice> displayDevice;
+    uint32_t textureName;
 };
 
-class Layer : public virtual compositionengine::LayerFE {
+class Layer : public virtual RefBase, compositionengine::LayerFE {
     static std::atomic<int32_t> sSequence;
+    static constexpr int32_t PRIORITY_UNSET = -1;
 
 public:
     mutable bool contentDirty{false};
-    // regions below are in window-manager space
-    Region visibleRegion;
-    Region coveredRegion;
-    Region visibleNonTransparentRegion;
     Region surfaceDamageRegion;
 
     // Layer serial number.  This gives layers an explicit ordering, so we
@@ -138,6 +135,34 @@
         float radius = 0.0f;
     };
 
+    // FrameRateCompatibility specifies how we should interpret the frame rate associated with
+    // the layer.
+    enum class FrameRateCompatibility {
+        Default, // Layer didn't specify any specific handling strategy
+
+        ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
+                         // content properly. Any other value will result in a pull down.
+
+        NoVote, // Layer doesn't have any requirements for the refresh rate and
+                // should not be considered when the display refresh rate is determined.
+    };
+
+    // Encapsulates the frame rate and compatibility of the layer. This information will be used
+    // when the display refresh rate is determined.
+    struct FrameRate {
+        float rate;
+        FrameRateCompatibility type;
+
+        FrameRate() : rate(0), type(FrameRateCompatibility::Default) {}
+        FrameRate(float rate, FrameRateCompatibility type) : rate(rate), type(type) {}
+
+        bool operator==(const FrameRate& other) const {
+            return rate == other.rate && type == other.type;
+        }
+
+        bool operator!=(const FrameRate& other) const { return !(*this == other); }
+    };
+
     struct State {
         Geometry active_legacy;
         Geometry requested_legacy;
@@ -174,21 +199,24 @@
 
         // If non-null, a Surface this Surface's Z-order is interpreted relative to.
         wp<Layer> zOrderRelativeOf;
+        bool isRelativeOf{false};
 
         // A list of surfaces whose Z-order is interpreted relative to ours.
         SortedVector<wp<Layer>> zOrderRelatives;
 
         half4 color;
         float cornerRadius;
+        int backgroundBlurRadius;
 
         bool inputInfoChanged;
         InputWindowInfo inputInfo;
         wp<Layer> touchableRegionCrop;
 
-        // dataspace is only used by BufferStateLayer and ColorLayer
+        // dataspace is only used by BufferStateLayer and EffectLayer
         ui::Dataspace dataspace;
 
         // The fields below this point are only used by BufferStateLayer
+        uint64_t frameNumber;
         Geometry active;
 
         uint32_t transform;
@@ -217,11 +245,25 @@
         // recent callback handle.
         std::deque<sp<CallbackHandle>> callbackHandles;
         bool colorSpaceAgnostic;
+        nsecs_t desiredPresentTime = -1;
+
+        // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
+        // be rendered around the layer.
+        float shadowRadius;
+
+        // Priority of the layer assigned by Window Manager.
+        int32_t frameRateSelectionPriority;
+
+        FrameRate frameRate;
     };
 
     explicit Layer(const LayerCreationArgs& args);
     virtual ~Layer();
 
+    void onFirstRef() override;
+
+    int getWindowType() const { return mWindowType; }
+
     void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; }
     bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; }
 
@@ -267,9 +309,9 @@
 
     // setPosition operates in parent buffer space (pre parent-transform) or display
     // space for top-level layers.
-    virtual bool setPosition(float x, float y, bool immediate);
+    virtual bool setPosition(float x, float y);
     // Buffer space
-    virtual bool setCrop_legacy(const Rect& crop, bool immediate);
+    virtual bool setCrop_legacy(const Rect& crop);
 
     // TODO(b/38182121): Could we eliminate the various latching modes by
     // using the layer hierarchy?
@@ -286,6 +328,9 @@
     // 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);
+    // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
+    // is specified in pixels.
+    virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
     virtual bool setTransparentRegionHint(const Region& transparent);
     virtual bool setFlags(uint8_t flags, uint8_t mask);
     virtual bool setLayerStack(uint32_t layerStack);
@@ -311,8 +356,8 @@
     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*/, nsecs_t /*postTime*/,
-                           nsecs_t /*desiredPresentTime*/,
+    virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/,
+                           nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/,
                            const client_cache_t& /*clientCacheId*/) {
         return false;
     };
@@ -326,10 +371,20 @@
             const std::vector<sp<CallbackHandle>>& /*handles*/) {
         return false;
     };
+    virtual void forceSendCallbacks() {}
+    virtual bool addFrameEvent(const sp<Fence>& /*acquireFence*/, nsecs_t /*postedTime*/,
+                               nsecs_t /*requestedPresentTime*/) {
+        return false;
+    }
     virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace);
     virtual bool setColorSpaceAgnostic(const bool agnostic);
+    bool setShadowRadius(float shadowRadius);
+    virtual bool setFrameRateSelectionPriority(int32_t priority);
+    //  If the variable is not set on the layer, it traverses up the tree to inherit the frame
+    //  rate priority from its parent.
+    virtual int32_t getFrameRateSelectionPriority() const;
 
-    ui::Dataspace getDataSpace() const { return mCurrentDataSpace; }
+    virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; }
 
     // Before color management is introduced, contents on Android have to be
     // desaturated in order to match what they appears like visually.
@@ -338,7 +393,8 @@
     // visually.
     bool isLegacyDataSpace() const;
 
-    virtual std::shared_ptr<compositionengine::Layer> getCompositionLayer() const;
+    virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const;
+    virtual compositionengine::LayerFECompositionState* editCompositionState();
 
     // 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
@@ -357,13 +413,11 @@
         return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay);
     }
 
-    void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh,
-                         bool useIdentityTransform) const;
     FloatRect getBounds(const Region& activeTransparentRegion) const;
     FloatRect getBounds() const;
 
     // Compute bounds for the layer and cache the results.
-    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform);
+    void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
 
     // Returns the buffer scale transform if a scaling mode is set.
     ui::Transform getBufferScaleTransform() const;
@@ -377,9 +431,19 @@
 
     int32_t getSequence() const { return sequence; }
 
+    // For tracing.
+    // TODO: Replace with raw buffer id from buffer metadata when that becomes available.
+    // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces
+    // creates its tracks by buffer id and has no way of associating a buffer back to the process
+    // that created it, the current implementation is only sufficient for cases where a buffer is
+    // only used within a single layer.
+    uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
+
     // -----------------------------------------------------------------------
     // Virtuals
-    virtual const char* getTypeId() const = 0;
+
+    // Provide unique string for each class type in the Layer hierarchy
+    virtual const char* getType() const = 0;
 
     /*
      * isOpaque - true if this surface is opaque
@@ -437,16 +501,14 @@
 
     bool isRemovedFromCurrentState() const;
 
+    LayerProto* writeToProto(LayersProto& layersProto,
+                             uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+
     // Write states that are modified by the main thread. This includes drawing
     // state as well as buffer data. This should be called in the main or tracing
     // thread.
     void writeToProtoDrawingState(LayerProto* layerInfo,
                                   uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
-    // Write states that are modified by the main thread. This includes drawing
-    // state as well as buffer data and composition data for layers on the specified
-    // display. This should be called in the main or tracing thread.
-    void writeToProtoCompositionState(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice,
-                                      uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
     // Write drawing or current state. If writing current state, the caller should hold the
     // external mStateLock. If writing drawing state, this function should be called on the
     // main or tracing thread.
@@ -463,60 +525,77 @@
         return s.activeTransparentRegion_legacy;
     }
     virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; }
+    virtual bool needsFiltering(const sp<const DisplayDevice>&) const { return false; }
+
+    // This layer is not a clone, but it's the parent to the cloned hierarchy. The
+    // variable mClonedChild represents the top layer that will be cloned so this
+    // layer will be the parent of mClonedChild.
+    // The layers in the cloned hierarchy will match the lifetime of the real layers. That is
+    // if the real layer is destroyed, then the clone layer will also be destroyed.
+    sp<Layer> mClonedChild;
+
+    virtual sp<Layer> createClone() = 0;
+    void updateMirrorInfo();
+    virtual void updateCloneBufferInfo(){};
 
 protected:
-    virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip,
-                                    bool useIdentityTransform, Region& clearRegion,
-                                    const bool supportProtectedContent,
-                                    renderengine::LayerSettings& layer);
+    sp<compositionengine::LayerFE> asLayerFE() const;
+    sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; }
+    bool isClone() { return mClonedFrom != nullptr; }
+    bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
+
+    virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom);
+
+    void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void updateClonedChildren(const sp<Layer>& mirrorRoot,
+                              std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    void addChildToDrawing(const sp<Layer>& layer);
+    void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
+    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&);
+    virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition(
+            const LayerFE::LayerSettings& layerSettings, const Rect& displayViewport,
+            ui::Dataspace outputDataspace);
+    // Modifies the passed in layer settings to clear the contents. If the blackout flag is set,
+    // the settings clears the content with a solid black fill.
+    void prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const;
 
 public:
     /*
      * compositionengine::LayerFE overrides
      */
-    void latchCompositionState(compositionengine::LayerFECompositionState&,
-                               bool includeGeometry) const override;
+    const compositionengine::LayerFECompositionState* getCompositionState() const override;
+    bool onPreComposition(nsecs_t) override;
+    void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override;
+    std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList(
+            compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
     void onLayerDisplayed(const sp<Fence>& releaseFence) override;
     const char* getDebugName() const override;
 
 protected:
-    void latchGeometry(compositionengine::LayerFECompositionState& outState) const;
+    void prepareBasicGeometryCompositionState();
+    void prepareGeometryCompositionState();
+    virtual void preparePerFrameCompositionState();
+    void prepareCursorCompositionState();
 
 public:
     virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {}
 
     virtual bool isHdrY410() const { return false; }
 
-    void forceClientComposition(const sp<DisplayDevice>& display);
-    bool getForceClientComposition(const sp<DisplayDevice>& display);
-    virtual void setPerFrameData(const sp<const DisplayDevice>& display,
-                                 const ui::Transform& transform, const Rect& viewport,
-                                 int32_t supportedPerFrameMetadata,
-                                 const ui::Dataspace targetDataspace) = 0;
-
-    // callIntoHwc exists so we can update our local state and call
-    // acceptDisplayChanges without unnecessarily updating the device's state
-    void setCompositionType(const sp<const DisplayDevice>& display,
-                            Hwc2::IComposerClient::Composition type);
     Hwc2::IComposerClient::Composition getCompositionType(
             const sp<const DisplayDevice>& display) const;
     bool getClearClientTarget(const sp<const DisplayDevice>& display) const;
-    void updateCursorPosition(const sp<const DisplayDevice>& display);
 
     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) = 0;
-
-    /*
      * called after composition.
      * returns true if the layer latched a new buffer this frame.
      */
-    virtual bool onPostComposition(const std::optional<DisplayId>& /*displayId*/,
+    virtual bool onPostComposition(sp<const DisplayDevice> /*displayDevice*/,
                                    const std::shared_ptr<FenceTime>& /*glDoneFence*/,
                                    const std::shared_ptr<FenceTime>& /*presentFence*/,
                                    const CompositorTiming& /*compositorTiming*/) {
@@ -526,17 +605,8 @@
     // If a buffer was replaced this frame, release the former buffer
     virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { }
 
-    /*
-     * prepareClientLayer - populates a renderengine::LayerSettings to passed to
-     * RenderEngine::drawLayers. Returns true if the layer can be used, and
-     * false otherwise.
-     */
-    bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion,
-                            const bool supportProtectedContent, renderengine::LayerSettings& layer);
-    bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform,
-                            Region& clearRegion, const bool supportProtectedContent,
-                            renderengine::LayerSettings& layer);
-
+    virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/,
+                                           const CompositorTiming& /*compositorTiming*/) {}
     /*
      * doTransaction - process the transaction. This is a good place to figure
      * out which attributes of the surface have changed.
@@ -544,41 +614,20 @@
     uint32_t doTransaction(uint32_t transactionFlags);
 
     /*
-     * setVisibleRegion - called to set the new visible region. This gives
-     * a chance to update the new visible region or record the fact it changed.
-     */
-    void setVisibleRegion(const Region& visibleRegion);
-
-    /*
-     * setCoveredRegion - called when the covered region changes. The covered
-     * region corresponds to any area of the surface that is covered
-     * (transparently or not) by another surface.
-     */
-    void setCoveredRegion(const Region& coveredRegion);
-
-    /*
-     * setVisibleNonTransparentRegion - called when the visible and
-     * non-transparent region changes.
-     */
-    void setVisibleNonTransparentRegion(const Region& visibleNonTransparentRegion);
-
-    /*
-     * Clear the visible, covered, and non-transparent regions.
-     */
-    void clearVisibilityRegions();
-
-    /*
      * 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.
      */
-    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) {
-        return {};
+    virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
+                             nsecs_t /*expectedPresentTime*/) {
+        return false;
     }
 
     virtual bool isBufferLatched() const { return false; }
 
+    virtual void latchAndReleaseBuffer() {}
+
     /*
      * Remove relative z for the layer if its relative parent is not part of the
      * provided layer tree.
@@ -609,7 +658,14 @@
      * returns the rectangle that crops the content of the layer and scales it
      * to the layer's size.
      */
-    Rect getContentCrop() const;
+    virtual Rect getBufferCrop() const { return Rect(); }
+
+    /*
+     * Returns the transform applied to the buffer.
+     */
+    virtual uint32_t getBufferTransform() const { return 0; }
+
+    virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
 
     /*
      * Returns if a frame is ready
@@ -619,10 +675,6 @@
     virtual int32_t getQueuedFrameCount() const { return 0; }
 
     // -----------------------------------------------------------------------
-
-    bool hasHwcLayer(const sp<const DisplayDevice>& displayDevice);
-    HWC2::Layer* getHwcLayer(const sp<const DisplayDevice>& displayDevice);
-
     inline const State& getDrawingState() const { return mDrawingState; }
     inline const State& getCurrentState() const { return mCurrentState; }
     inline State& getCurrentState() { return mCurrentState; }
@@ -634,6 +686,7 @@
     void miniDump(std::string& result, const sp<DisplayDevice>& display) const;
     void dumpFrameStats(std::string& result) const;
     void dumpFrameEvents(std::string& result);
+    void dumpCallingUidPid(std::string& result) const;
     void clearFrameStats();
     void logFrameStats();
     void getFrameStats(FrameStats* outStats) const;
@@ -655,14 +708,27 @@
     // down the hierarchy).
     half getAlpha() const;
     half4 getColor() const;
+    int32_t getBackgroundBlurRadius() const;
+    bool drawShadows() const { return mEffectiveShadowRadius > 0.f; };
 
     // 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;
+    virtual RoundedCornerState getRoundedCornerState() const;
 
+    renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+
+    /**
+     * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder
+     * which will not emit children who have relativeZOrder to another layer, this method
+     * just directly emits all children. It also emits them in no particular order.
+     * So this method is not suitable for graphical operations, as it doesn't represent
+     * the scene state, but it's also more efficient than traverseInZOrder and so useful for
+     * book-keeping.
+     */
+    void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
     void traverseInReverseZOrder(LayerVector::StateSet stateSet,
                                  const LayerVector::Visitor& visitor);
     void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -675,6 +741,15 @@
                                   const LayerVector::Visitor& visitor);
 
     size_t getChildrenCount() const;
+
+    // ONLY CALL THIS FROM THE LAYER DTOR!
+    // See b/141111965.  We need to add current children to offscreen layers in
+    // the layer dtor so as not to dangle layers.  Since the layer has not
+    // committed its transaction when the layer is destroyed, we must add
+    // current children.  This is safe in the dtor as we will no longer update
+    // the current state, but should not be called anywhere else!
+    LayerVector& getCurrentChildren() { return mCurrentChildren; }
+
     void addChild(const sp<Layer>& layer);
     // Returns index if removed, or negative value otherwise
     // for symmetry with Vector::remove
@@ -689,7 +764,7 @@
     // Copy the current list of children to the drawing state. Called by
     // SurfaceFlinger to complete a transaction.
     void commitChildList();
-    int32_t getZ() const;
+    int32_t getZ(LayerVector::StateSet stateSet) const;
     virtual void pushPendingState();
 
     /**
@@ -710,6 +785,19 @@
     compositionengine::OutputLayer* findOutputLayerForDisplay(
             const sp<const DisplayDevice>& display) const;
 
+    Region debugGetVisibleRegionOnDefaultDisplay() 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;
+
+    virtual bool setFrameRate(FrameRate frameRate);
+    virtual FrameRate getFrameRate() const;
+
 protected:
     // constant
     sp<SurfaceFlinger> mFlinger;
@@ -736,6 +824,7 @@
 
     // For unit tests
     friend class TestableSurfaceFlinger;
+    friend class RefreshRateSelectionTest;
 
     virtual void commitTransaction(const State& stateToCommit);
 
@@ -820,8 +909,8 @@
     // Creates a new handle each time, so we only expect
     // this to be called once.
     sp<IBinder> getHandle();
-    const String8& getName() const;
-    virtual void notifyAvailableFrames() {}
+    const std::string& getName() const { return mName; }
+    virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {}
     virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; }
     bool getPremultipledAlpha() const;
 
@@ -836,8 +925,8 @@
     bool usingRelativeZ(LayerVector::StateSet stateSet) const;
 
     bool mPremultipliedAlpha{true};
-    String8 mName;
-    String8 mTransactionName; // A cached version of "TX - " + mName for systraces
+    const std::string mName;
+    const std::string mTransactionName{"TX - " + mName};
 
     bool mPrimaryDisplayOnly = false;
 
@@ -864,20 +953,13 @@
 
     // main thread
     sp<NativeHandle> mSidebandStream;
-    // Active buffer fields
-    sp<GraphicBuffer> mActiveBuffer;
-    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{0};
     // We encode unset as -1.
     int32_t mOverrideScalingMode{-1};
     std::atomic<uint64_t> mCurrentFrameNumber{0};
-    bool mFrameLatencyNeeded{false};
     // Whether filtering is needed b/c of the drawingstate
     bool mNeedsFiltering{false};
 
@@ -894,8 +976,6 @@
     // This layer can be a cursor on some displays.
     bool mPotentialCursor{false};
 
-    bool mFreezeGeometryUpdates{false};
-
     // Child list about to be committed/used for editing.
     LayerVector mCurrentChildren{LayerVector::StateSet::Current};
     // Child list used for rendering.
@@ -912,9 +992,6 @@
     // Window types from WindowManager.LayoutParams
     const int mWindowType;
 
-    // This is populated if the layer is registered with Scheduler for tracking purposes.
-    std::unique_ptr<scheduler::LayerHistory::LayerHandle> mSchedulerLayerHandle;
-
 private:
     /**
      * Returns an unsorted vector of all layers that are part of this tree.
@@ -930,13 +1007,6 @@
                                        const LayerVector::Visitor& visitor);
     LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
                                           const std::vector<Layer*>& layersInTree);
-    /**
-     * 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;
 
     // Cached properties computed from drawing state
     // Effective transform taking into account parent transforms and any parent scaling.
@@ -958,6 +1028,29 @@
     bool mGetHandleCalled = false;
 
     void removeRemoteSyncPoints();
+
+    // Tracks the process and user id of the caller when creating this layer
+    // to help debugging.
+    pid_t mCallingPid;
+    uid_t mCallingUid;
+
+    // The current layer is a clone of mClonedFrom. This means that this layer will update it's
+    // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers,
+    // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children,
+    // and relatives, this layer will update as well.
+    wp<Layer> mClonedFrom;
+
+    // The inherited shadow radius after taking into account the layer hierarchy. This is the
+    // final shadow radius for this layer. If a shadow is specified for a layer, then effective
+    // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
+    float mEffectiveShadowRadius = 0.f;
+
+    // Returns true if the layer can draw shadows on its border.
+    virtual bool canDrawShadows() const { return true; }
+
+    // Find the root of the cloned hierarchy, this means the first non cloned parent.
+    // This will return null if first non cloned parent is not found.
+    sp<Layer> getClonedRoot();
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index c94e439..0fe1421 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "LayerProtoHelper.h"
 
 namespace android {
@@ -155,5 +159,16 @@
     }
 }
 
+void LayerProtoHelper::writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto) {
+    for (int i = 0; i < mat4::ROW_SIZE; i++) {
+        for (int j = 0; j < mat4::COL_SIZE; j++) {
+            colorTransformProto->add_val(matrix[i][j]);
+        }
+    }
+}
+
 } // namespace surfaceflinger
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 1754a3f..502238d 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -43,6 +43,7 @@
     static void writeToProto(const InputWindowInfo& inputInfo,
                              const wp<Layer>& touchableRegionBounds,
                              std::function<InputWindowInfoProto*()> getInputWindowInfoProto);
+    static void writeToProto(const mat4 matrix, ColorTransformProto* colorTransformProto);
 };
 
 } // namespace surfaceflinger
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index 72abea8..d3364a0 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "LayerRejecter.h"
 
 #include <gui/BufferItem.h>
@@ -23,22 +27,16 @@
 
 namespace android {
 
-LayerRejecter::LayerRejecter(Layer::State& front,
-                             Layer::State& current,
-                             bool& recomputeVisibleRegions,
-                             bool stickySet,
-                             const char* name,
-                             int32_t overrideScalingMode,
-                             bool transformToDisplayInverse,
-                             bool& freezePositionUpdates)
-  : mFront(front),
-    mCurrent(current),
-    mRecomputeVisibleRegions(recomputeVisibleRegions),
-    mStickyTransformSet(stickySet),
-    mName(name),
-    mOverrideScalingMode(overrideScalingMode),
-    mTransformToDisplayInverse(transformToDisplayInverse),
-    mFreezeGeometryUpdates(freezePositionUpdates) {}
+LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current,
+                             bool& recomputeVisibleRegions, bool stickySet, const std::string& name,
+                             int32_t overrideScalingMode, bool transformToDisplayInverse)
+      : mFront(front),
+        mCurrent(current),
+        mRecomputeVisibleRegions(recomputeVisibleRegions),
+        mStickyTransformSet(stickySet),
+        mName(name),
+        mOverrideScalingMode(overrideScalingMode),
+        mTransformToDisplayInverse(transformToDisplayInverse) {}
 
 bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) {
     if (buf == nullptr) {
@@ -55,7 +53,7 @@
     }
 
     if (mTransformToDisplayInverse) {
-        uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform();
+        uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags();
         if (invTransform & ui::Transform::ROT_90) {
             std::swap(bufWidth, bufHeight);
         }
@@ -83,8 +81,6 @@
             // recompute visible region
             mRecomputeVisibleRegions = true;
 
-            mFreezeGeometryUpdates = false;
-
             if (mFront.crop_legacy != mFront.requestedCrop_legacy) {
                 mFront.crop_legacy = mFront.requestedCrop_legacy;
                 mCurrent.crop_legacy = mFront.requestedCrop_legacy;
@@ -98,7 +94,7 @@
                  "(%4d,%4d) "
                  "}\n"
                  "            requested_legacy={ wh={%4u,%4u} }}\n",
-                 mName, bufWidth, bufHeight, item.mTransform, item.mScalingMode,
+                 mName.c_str(), 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(),
@@ -110,7 +106,8 @@
             // reject this buffer
             ALOGE("[%s] rejecting buffer: "
                   "bufWidth=%d, bufHeight=%d, front.active_legacy.{w=%d, h=%d}",
-                  mName, bufWidth, bufHeight, mFront.active_legacy.w, mFront.active_legacy.h);
+                  mName.c_str(), bufWidth, bufHeight, mFront.active_legacy.w,
+                  mFront.active_legacy.h);
             return true;
         }
     }
@@ -143,3 +140,6 @@
 }
 
 }  // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h
index 63d51de..fb5c750 100644
--- a/services/surfaceflinger/LayerRejecter.h
+++ b/services/surfaceflinger/LayerRejecter.h
@@ -14,36 +14,29 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_LAYER_REJECTER_H
-#define ANDROID_LAYER_REJECTER_H
+#pragma once
 
 #include "Layer.h"
 #include "BufferLayerConsumer.h"
 
 namespace android {
-    class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
-    public:
-        LayerRejecter(Layer::State &front,
-                      Layer::State &current,
-                      bool &recomputeVisibleRegions,
-                      bool stickySet,
-                      const char *name,
-                      int32_t overrideScalingMode,
-                      bool transformToDisplayInverse,
-                      bool &freezePositionUpdates);
 
-        virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item);
+class LayerRejecter : public BufferLayerConsumer::BufferRejecter {
+public:
+    LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions,
+                  bool stickySet, const std::string& name, int32_t overrideScalingMode,
+                  bool transformToDisplayInverse);
 
-    private:
-        Layer::State &mFront;
-        Layer::State &mCurrent;
-        bool &mRecomputeVisibleRegions;
-        bool mStickyTransformSet;
-        const char *mName;
-        int32_t mOverrideScalingMode;
-        bool mTransformToDisplayInverse;
-        bool &mFreezeGeometryUpdates;
-    };
-}  // namespace android
+    virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&);
 
-#endif  // ANDROID_LAYER_REJECTER_H
+private:
+    Layer::State& mFront;
+    Layer::State& mCurrent;
+    bool& mRecomputeVisibleRegions;
+    const bool mStickyTransformSet;
+    const std::string& mName;
+    const int32_t mOverrideScalingMode;
+    const bool mTransformToDisplayInverse;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/LayerStats.cpp b/services/surfaceflinger/LayerStats.cpp
deleted file mode 100644
index a2d1feb..0000000
--- a/services/surfaceflinger/LayerStats.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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 "LayerStats"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "LayerStats.h"
-#include "DisplayHardware/HWComposer.h"
-#include "ui/DebugUtils.h"
-
-#include <android-base/stringprintf.h>
-#include <log/log.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);
-    if (mEnabled) return;
-    mLayerShapeStatsMap.clear();
-    mEnabled = true;
-    ALOGD("Logging enabled");
-}
-
-void LayerStats::disable() {
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mEnabled) return;
-    mEnabled = false;
-    ALOGD("Logging disabled");
-}
-
-void LayerStats::clear() {
-    ATRACE_CALL();
-    std::lock_guard<std::mutex> lock(mMutex);
-    mLayerShapeStatsMap.clear();
-    ALOGD("Cleared current layer stats");
-}
-
-bool LayerStats::isEnabled() {
-    return mEnabled;
-}
-
-void LayerStats::traverseLayerTreeStatsLocked(
-        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 = "";
-        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());
-    }
-}
-
-void LayerStats::logLayerStats(const LayersProto& layersProto) {
-    ATRACE_CALL();
-    ALOGV("Logging");
-    auto layerGlobal = LayerProtoParser::generateLayerGlobalInfo(layersProto);
-    auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
-    std::vector<std::string> layerShapeVec;
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    traverseLayerTreeStatsLocked(layerTree.topLevelLayers, layerGlobal, &layerShapeVec);
-
-    std::string layerShapeKey =
-            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>());
-    for (auto const& s : layerShapeVec) {
-        layerShapeKey += s;
-    }
-
-    mLayerShapeStatsMap[layerShapeKey]++;
-}
-
-void LayerStats::dump(std::string& result) {
-    ATRACE_CALL();
-    ALOGD("Dumping");
-    std::lock_guard<std::mutex> lock(mMutex);
-    result.append("Frequency,LayerCount,ColorMode,ColorTransform,Orientation\n");
-    result.append("LayerType,CompositionType,IsProtected,Transform,PixelFormat,Dataspace,");
-    result.append("DstX,DstY,DstWidth,DstHeight,WScale,HScale,Alpha\n");
-    for (auto& u : mLayerShapeStatsMap) {
-        StringAppendF(&result, "%u,%s\n", u.second, u.first.c_str());
-    }
-}
-
-const char* LayerStats::destinationLocation(int32_t location, int32_t range, bool isHorizontal) {
-    static const char* locationArray[8] = {"0", "1/8", "1/4", "3/8", "1/2", "5/8", "3/4", "7/8"};
-    int32_t ratio = location * 8 / range;
-    if (ratio < 0) return "N/A";
-    if (isHorizontal) {
-        // X location is divided into 4 buckets {"0", "1/4", "1/2", "3/4"}
-        if (ratio > 6) return "3/4";
-        // use index 0, 2, 4, 6
-        return locationArray[ratio & ~1];
-    }
-    if (ratio > 7) return "7/8";
-    return locationArray[ratio];
-}
-
-const char* LayerStats::destinationSize(int32_t size, int32_t range, bool isWidth) {
-    static const char* sizeArray[8] = {"1/8", "1/4", "3/8", "1/2", "5/8", "3/4", "7/8", "1"};
-    int32_t ratio = size * 8 / range;
-    if (ratio < 0) return "N/A";
-    if (isWidth) {
-        // width is divided into 4 buckets {"1/4", "1/2", "3/4", "1"}
-        if (ratio > 6) return "1";
-        // use index 1, 3, 5, 7
-        return sizeArray[ratio | 1];
-    }
-    if (ratio > 7) return "1";
-    return sizeArray[ratio];
-}
-
-const char* LayerStats::layerTransform(int32_t transform) {
-    return getTransformName(static_cast<hwc_transform_t>(transform));
-}
-
-const char* LayerStats::layerCompositionType(int32_t compositionType) {
-    return getCompositionName(static_cast<hwc2_composition_t>(compositionType));
-}
-
-std::string LayerStats::layerPixelFormat(int32_t pixelFormat) {
-    return decodePixelFormat(pixelFormat);
-}
-
-std::string LayerStats::scaleRatioWH(const LayerProtoParser::Layer* layer) {
-    if (!layer->type.compare("ColorLayer")) return "N/A,N/A";
-    std::string ret = "";
-    if (isRotated(layer->hwcTransform)) {
-        ret += scaleRatio(layer->hwcFrame.right - layer->hwcFrame.left,
-                          static_cast<int32_t>(layer->hwcCrop.bottom - layer->hwcCrop.top));
-        ret += ",";
-        ret += scaleRatio(layer->hwcFrame.bottom - layer->hwcFrame.top,
-                          static_cast<int32_t>(layer->hwcCrop.right - layer->hwcCrop.left));
-    } else {
-        ret += scaleRatio(layer->hwcFrame.right - layer->hwcFrame.left,
-                          static_cast<int32_t>(layer->hwcCrop.right - layer->hwcCrop.left));
-        ret += ",";
-        ret += scaleRatio(layer->hwcFrame.bottom - layer->hwcFrame.top,
-                          static_cast<int32_t>(layer->hwcCrop.bottom - layer->hwcCrop.top));
-    }
-    return ret;
-}
-
-const char* LayerStats::scaleRatio(int32_t destinationScale, int32_t sourceScale) {
-    // Make scale buckets from <1/64 to >= 16, to avoid floating point
-    // calculation, x64 on destinationScale first
-    int32_t scale = destinationScale * 64 / sourceScale;
-    if (!scale) return "<1/64";
-    if (scale < 2) return "1/64";
-    if (scale < 4) return "1/32";
-    if (scale < 8) return "1/16";
-    if (scale < 16) return "1/8";
-    if (scale < 32) return "1/4";
-    if (scale < 64) return "1/2";
-    if (scale < 128) return "1";
-    if (scale < 256) return "2";
-    if (scale < 512) return "4";
-    if (scale < 1024) return "8";
-    return ">=16";
-}
-
-const char* LayerStats::alpha(float a) {
-    if (a == 1.0f) return "1.0";
-    if (a > 0.9f) return "0.99";
-    if (a > 0.8f) return "0.9";
-    if (a > 0.7f) return "0.8";
-    if (a > 0.6f) return "0.7";
-    if (a > 0.5f) return "0.6";
-    if (a > 0.4f) return "0.5";
-    if (a > 0.3f) return "0.4";
-    if (a > 0.2f) return "0.3";
-    if (a > 0.1f) return "0.2";
-    if (a > 0.0f) return "0.1";
-    return "0.0";
-}
-
-bool LayerStats::isRotated(int32_t transform) {
-    return transform & HWC_TRANSFORM_ROT_90;
-}
-
-bool LayerStats::isVFlipped(int32_t transform) {
-    return transform & HWC_TRANSFORM_FLIP_V;
-}
-
-bool LayerStats::isHFlipped(int32_t transform) {
-    return transform & HWC_TRANSFORM_FLIP_H;
-}
-
-}  // namespace android
diff --git a/services/surfaceflinger/LayerStats.h b/services/surfaceflinger/LayerStats.h
deleted file mode 100644
index 62b2688..0000000
--- a/services/surfaceflinger/LayerStats.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 <layerproto/LayerProtoHeader.h>
-#include <layerproto/LayerProtoParser.h>
-#include <mutex>
-#include <unordered_map>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-class LayerStats {
-public:
-    void enable();
-    void disable();
-    void clear();
-    bool isEnabled();
-    void logLayerStats(const LayersProto& layersProto);
-    void dump(std::string& result);
-
-private:
-    // Traverse layer tree to get all visible layers' stats
-    void traverseLayerTreeStatsLocked(
-            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
-    static const char* destinationLocation(int32_t location, int32_t range, bool isHorizontal);
-    // Convert layer's size into 8x8 percentage of the display
-    static const char* destinationSize(int32_t size, int32_t range, bool isWidth);
-    // Return the name of the transform
-    static const char* layerTransform(int32_t transform);
-    // Return the name of the composition type
-    static const char* layerCompositionType(int32_t compositionType);
-    // Return the name of the pixel format
-    static std::string layerPixelFormat(int32_t pixelFormat);
-    // Calculate scale ratios of layer's width/height with rotation information
-    static std::string scaleRatioWH(const LayerProtoParser::Layer* layer);
-    // Calculate scale ratio from source to destination and convert to string
-    static const char* scaleRatio(int32_t destinationScale, int32_t sourceScale);
-    // Bucket the alpha into designed buckets
-    static const char* alpha(float a);
-    // Return whether the original buffer is rotated in final composition
-    static bool isRotated(int32_t transform);
-    // Return whether the original buffer is V-flipped in final composition
-    static bool isVFlipped(int32_t transform);
-    // Return whether the original buffer is H-flipped in final composition
-    static bool isHFlipped(int32_t transform);
-
-    bool mEnabled = false;
-    // Protect mLayersStatsMap
-    std::mutex mMutex;
-    // Hashmap for tracking the frame(layer shape) stats
-    // KEY is a concatenation of all layers' properties within a frame
-    // VALUE is the number of times this particular set has been scanned out
-    std::unordered_map<std::string, uint32_t> mLayerShapeStatsMap;
-};
-
-}  // namespace android
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index 8494524..9b94920 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "LayerVector.h"
 #include "Layer.h"
 
@@ -64,7 +68,7 @@
         const auto& layer = (*this)[i];
         auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
                                                       : layer->getDrawingState();
-        if (state.zOrderRelativeOf != nullptr) {
+        if (state.isRelativeOf) {
             continue;
         }
         layer->traverseInZOrder(stateSet, visitor);
@@ -76,10 +80,21 @@
         const auto& layer = (*this)[i];
         auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState()
                                                       : layer->getDrawingState();
-        if (state.zOrderRelativeOf != nullptr) {
+        if (state.isRelativeOf) {
             continue;
         }
         layer->traverseInReverseZOrder(stateSet, visitor);
      }
 }
+
+void LayerVector::traverse(const Visitor& visitor) const {
+    for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) {
+        const auto& layer = (*this)[i];
+        layer->traverse(mStateSet, visitor);
+    }
+}
+
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 88d7711..a531f4f 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -50,7 +50,7 @@
     using Visitor = std::function<void(Layer*)>;
     void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const;
     void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const;
-
+    void traverse(const Visitor& visitor) const;
 private:
     const StateSet mStateSet;
 };
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index c60421b..40a63d7 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "MonitoredProducer.h"
 #include "Layer.h"
 #include "SurfaceFlinger.h"
@@ -154,6 +158,14 @@
     return mProducer->getConsumerUsage(outUsage);
 }
 
+status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) {
+    return mProducer->setAutoPrerotation(autoPrerotation);
+}
+
+status_t MonitoredProducer::setFrameRate(float frameRate) {
+    return mProducer->setFrameRate(frameRate);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return this;
 }
@@ -164,3 +176,6 @@
 
 // ---------------------------------------------------------------------------
 }; // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index d346f82..4bda831 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -70,6 +70,8 @@
     virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override;
     virtual status_t getUniqueId(uint64_t* outId) const override;
     virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
+    virtual status_t setAutoPrerotation(bool autoPrerotation) override;
+    virtual status_t setFrameRate(float frameRate) override;
 
     // The Layer which created this producer, and on which queued Buffer's will be displayed.
     sp<Layer> getLayer() const;
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 5b4bec9..682679c 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -14,32 +14,148 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "RefreshRateOverlay.h"
 #include "Client.h"
 #include "Layer.h"
 
+#include <gui/IProducerListener.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateOverlay"
+
 namespace android {
 
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
+                                                      const sp<GraphicBuffer>& buffer,
+                                                      uint8_t* pixels) {
+    for (int32_t j = r.top; j < r.bottom; j++) {
+        if (j >= buffer->getHeight()) {
+            break;
+        }
+
+        for (int32_t i = r.left; i < r.right; i++) {
+            if (i >= buffer->getWidth()) {
+                break;
+            }
+
+            uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
+            iter[0] = uint8_t(color.r * 255);
+            iter[1] = uint8_t(color.g * 255);
+            iter[2] = uint8_t(color.b * 255);
+            iter[3] = uint8_t(color.a * 255);
+        }
+    }
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
+                                                         const half4& color,
+                                                         const sp<GraphicBuffer>& buffer,
+                                                         uint8_t* pixels) {
+    const Rect rect = [&]() {
+        switch (segment) {
+            case Segment::Upper:
+                return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+            case Segment::UpperLeft:
+                return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+            case Segment::UpperRight:
+                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
+                            DIGIT_HEIGHT / 2);
+            case Segment::Middle:
+                return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
+                            DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+            case Segment::LowerLeft:
+                return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+            case Segment::LowerRight:
+                return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
+                            DIGIT_HEIGHT);
+            case Segment::Buttom:
+                return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+        }
+    }();
+
+    drawRect(rect, color, buffer, pixels);
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
+                                                       const sp<GraphicBuffer>& buffer,
+                                                       uint8_t* pixels) {
+    if (digit < 0 || digit > 9) return;
+
+    if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
+        digit == 8 || digit == 9)
+        drawSegment(Segment::Upper, left, color, buffer, pixels);
+    if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
+        drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+    if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
+        digit == 8 || digit == 9)
+        drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+    if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
+        digit == 9)
+        drawSegment(Segment::Middle, left, color, buffer, pixels);
+    if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
+        drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+    if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
+        digit == 7 || digit == 8 || digit == 9)
+        drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+    if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
+        digit == 9)
+        drawSegment(Segment::Buttom, left, color, buffer, pixels);
+}
+
+sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
+                                                                     const half4& color) {
+    if (number < 0 || number > 1000) return nullptr;
+
+    const auto hundreds = number / 100;
+    const auto tens = (number / 10) % 10;
+    const auto ones = number % 10;
+
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                              GRALLOC_USAGE_SW_WRITE_RARELY, "RefreshRateOverlayBuffer");
+    uint8_t* pixels;
+    buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+    int left = 0;
+    if (hundreds != 0) {
+        drawDigit(hundreds, left, color, buffer, pixels);
+        left += DIGIT_WIDTH + DIGIT_SPACE;
+    }
+
+    if (tens != 0) {
+        drawDigit(tens, left, color, buffer, pixels);
+        left += DIGIT_WIDTH + DIGIT_SPACE;
+    }
+
+    drawDigit(ones, left, color, buffer, pixels);
+    buffer->unlock();
+    return buffer;
+}
 
 RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
       : mFlinger(flinger), mClient(new Client(&mFlinger)) {
     createLayer();
+    primeCache();
 }
 
 bool RefreshRateOverlay::createLayer() {
     const status_t ret =
-            mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0,
-                                 PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor,
-                                 LayerMetadata(), &mIBinder, &mGbp, nullptr);
+            mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
+                                 SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
+                                 PIXEL_FORMAT_RGBA_8888,
+                                 ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
+                                 &mIBinder, &mGbp, nullptr);
     if (ret) {
-        ALOGE("failed to create color layer");
+        ALOGE("failed to create buffer state layer");
         return false;
     }
 
     Mutex::Autolock _l(mFlinger.mStateLock);
     mLayer = mClient->getLayerUser(mIBinder);
-    mLayer->setCrop_legacy(Rect(50, 70, 200, 100), true);
+    mLayer->setFrameRate(Layer::FrameRate(0, Layer::FrameRateCompatibility::NoVote));
 
     // setting Layer's Z requires resorting layersSortedByZ
     ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -51,10 +167,55 @@
     return true;
 }
 
-void RefreshRateOverlay::changeRefreshRate(RefreshRateType type) {
-    const half3& color = (type == RefreshRateType::PERFORMANCE) ? GREEN : RED;
-    mLayer->setColor(color);
+void RefreshRateOverlay::primeCache() {
+    auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
+    if (allRefreshRates.size() == 1) {
+        auto fps = allRefreshRates.begin()->second.fps;
+        half4 color = {LOW_FPS_COLOR, ALPHA};
+        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+        return;
+    }
+
+    std::vector<uint32_t> supportedFps;
+    supportedFps.reserve(allRefreshRates.size());
+    for (auto [ignored, refreshRate] : allRefreshRates) {
+        supportedFps.push_back(refreshRate.fps);
+    }
+
+    std::sort(supportedFps.begin(), supportedFps.end());
+    const auto mLowFps = supportedFps[0];
+    const auto mHighFps = supportedFps[supportedFps.size() - 1];
+    for (auto fps : supportedFps) {
+        const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps);
+        half4 color;
+        color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
+        color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
+        color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
+        color.a = ALPHA;
+        mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+    }
+}
+
+void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
+    const auto display = mFlinger.getDefaultDisplayDeviceLocked();
+    if (!display) {
+        return;
+    }
+
+    const int32_t left = display->getWidth() / 32;
+    const int32_t top = display->getHeight() / 32;
+    const int32_t right = left + display->getWidth() / 8;
+    const int32_t buttom = top + display->getHeight() / 32;
+
+    auto buffer = mBufferCache[refreshRate.fps];
+    mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {});
+
+    mLayer->setFrame(Rect(left, top, right, buttom));
+
     mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
 }
 
 }; // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index ce29bc3..6d34df2 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,16 +19,41 @@
 
 namespace android {
 
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
 
 class RefreshRateOverlay {
 public:
     RefreshRateOverlay(SurfaceFlinger& flinger);
 
-    void changeRefreshRate(RefreshRateType type);
+    void changeRefreshRate(const RefreshRate& refreshRate);
 
 private:
+    class SevenSegmentDrawer {
+    public:
+        static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+        static uint32_t getHeight() { return BUFFER_HEIGHT; }
+        static uint32_t getWidth() { return BUFFER_WIDTH; }
+
+    private:
+        enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom };
+
+        static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer,
+                             uint8_t* pixels);
+        static void drawSegment(Segment segment, int left, const half4& color,
+                                const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+        static void drawDigit(int digit, int left, const half4& color,
+                              const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+
+        static constexpr uint32_t DIGIT_HEIGHT = 100;
+        static constexpr uint32_t DIGIT_WIDTH = 64;
+        static constexpr uint32_t DIGIT_SPACE = 16;
+        static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
+        static constexpr uint32_t BUFFER_WIDTH =
+                3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+    };
+
     bool createLayer();
+    void primeCache();
 
     SurfaceFlinger& mFlinger;
     sp<Client> mClient;
@@ -36,8 +61,11 @@
     sp<IBinder> mIBinder;
     sp<IGraphicBufferProducer> mGbp;
 
-    const half3 RED = half3(1.0f, 0.0f, 0.0f);
-    const half3 GREEN = half3(0.0f, 1.0f, 0.0f);
+    std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
+
+    static constexpr float ALPHA = 0.8f;
+    const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
+    const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 07fdead..0031d70 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 #undef LOG_TAG
@@ -21,15 +25,18 @@
 
 #include "RegionSamplingThread.h"
 
-#include <cutils/properties.h>
-#include <gui/IRegionSamplingListener.h>
-#include <utils/Trace.h>
-#include <string>
-
 #include <compositionengine/Display.h>
 #include <compositionengine/impl/OutputCompositionState.h>
+#include <cutils/properties.h>
+#include <gui/IRegionSamplingListener.h>
+#include <ui/DisplayStatInfo.h>
+#include <utils/Trace.h>
+
+#include <string>
+
 #include "DisplayDevice.h"
 #include "Layer.h"
+#include "Scheduler/DispSync.h"
 #include "SurfaceFlinger.h"
 
 namespace android {
@@ -105,9 +112,8 @@
         if (mVsyncListening) return;
 
         mPhaseIntervalSetting = Phase::ZERO;
-        mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
-            sync.addEventListener("SamplingThreadDispSyncListener", 0, this, mLastCallbackTime);
-        });
+        mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this,
+                                                         mLastCallbackTime);
         mVsyncListening = true;
     }
 
@@ -120,9 +126,7 @@
     void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
         if (!mVsyncListening) return;
 
-        mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
-            sync.removeEventListener(this, &mLastCallbackTime);
-        });
+        mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime);
         mVsyncListening = false;
     }
 
@@ -132,16 +136,13 @@
         if (mPhaseIntervalSetting == Phase::ZERO) {
             ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
             mPhaseIntervalSetting = Phase::SAMPLING;
-            mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
-                sync.changePhaseOffset(this, mTargetSamplingOffset.count());
-            });
+            mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count());
             return;
         }
 
         if (mPhaseIntervalSetting == Phase::SAMPLING) {
             mPhaseIntervalSetting = Phase::ZERO;
-            mScheduler.withPrimaryDispSync(
-                    [this](android::DispSync& sync) { sync.changePhaseOffset(this, 0); });
+            mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0);
             stopVsyncListenerLocked();
             lock.unlock();
             mRegionSamplingThread.notifySamplingOffset();
@@ -271,16 +272,6 @@
     mDescriptors.erase(who);
 }
 
-namespace {
-// Using Rec. 709 primaries
-inline float getLuma(float r, float g, float b) {
-    constexpr auto rec709_red_primary = 0.2126f;
-    constexpr auto rec709_green_primary = 0.7152f;
-    constexpr auto rec709_blue_primary = 0.0722f;
-    return rec709_red_primary * r + rec709_green_primary * g + rec709_blue_primary * b;
-}
-} // anonymous namespace
-
 float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
                  uint32_t orientation, const Rect& sample_area) {
     if (!sample_area.isValid() || (sample_area.getWidth() > width) ||
@@ -301,30 +292,23 @@
         std::swap(area.left, area.right);
     }
 
-    std::array<int32_t, 256> brightnessBuckets = {};
-    const int32_t majoritySampleNum = area.getWidth() * area.getHeight() / 2;
+    const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left);
+    uint32_t accumulatedLuma = 0;
 
+    // Calculates luma with approximation of Rec. 709 primaries
     for (int32_t row = area.top; row < area.bottom; ++row) {
         const uint32_t* rowBase = data + row * stride;
         for (int32_t column = area.left; column < area.right; ++column) {
             uint32_t pixel = rowBase[column];
-            const float r = pixel & 0xFF;
-            const float g = (pixel >> 8) & 0xFF;
-            const float b = (pixel >> 16) & 0xFF;
-            const uint8_t luma = std::round(getLuma(r, g, b));
-            ++brightnessBuckets[luma];
-            if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f;
+            const uint32_t r = pixel & 0xFF;
+            const uint32_t g = (pixel >> 8) & 0xFF;
+            const uint32_t b = (pixel >> 16) & 0xFF;
+            const uint32_t luma = (r * 7 + b * 2 + g * 23) >> 5;
+            accumulatedLuma += luma;
         }
     }
 
-    int32_t accumulated = 0;
-    size_t bucket = 0;
-    for (; bucket < brightnessBuckets.size(); bucket++) {
-        accumulated += brightnessBuckets[bucket];
-        if (accumulated > majoritySampleNum) break;
-    }
-
-    return bucket / 255.0f;
+    return accumulatedLuma / (255.0f * pixelCount);
 }
 
 std::vector<float> RegionSamplingThread::sampleBuffer(
@@ -357,19 +341,7 @@
     }
 
     const auto device = mFlinger.getDefaultDisplayDevice();
-    const auto orientation = [](uint32_t orientation) {
-        switch (orientation) {
-            default:
-            case DisplayState::eOrientationDefault:
-                return ui::Transform::ROT_0;
-            case DisplayState::eOrientation90:
-                return ui::Transform::ROT_90;
-            case DisplayState::eOrientation180:
-                return ui::Transform::ROT_180;
-            case DisplayState::eOrientation270:
-                return ui::Transform::ROT_270;
-        }
-    }(device->getOrientation());
+    const auto orientation = ui::Transform::toRotationFlags(device->getOrientation());
 
     std::vector<RegionSamplingThread::Descriptor> descriptors;
     Region sampleRegion;
@@ -439,7 +411,7 @@
             }
             if (!intersectsAnyArea) return;
 
-            ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getName().string(), bounds.left,
+            ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left,
                   bounds.top, bounds.right, bounds.bottom);
             visitor(layer);
         };
@@ -504,3 +476,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index 96ffe20..99c07c2 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -27,7 +27,7 @@
 #include <ui/GraphicBuffer.h>
 #include <ui/Rect.h>
 #include <utils/StrongPointer.h>
-#include "Scheduler/IdleTimer.h"
+#include "Scheduler/OneShotTimer.h"
 
 namespace android {
 
@@ -107,7 +107,7 @@
     SurfaceFlinger& mFlinger;
     Scheduler& mScheduler;
     const TimingTunables mTunables;
-    scheduler::IdleTimer mIdleTimer;
+    scheduler::OneShotTimer mIdleTimer;
 
     std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
 
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 93759e8..9a6c853 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -1,3 +1,23 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "RenderArea.h"
 
 namespace android {
@@ -13,3 +33,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index edc6442..9b3a9f4 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -17,18 +17,21 @@
 // physical render area.
 class RenderArea {
 public:
+    using RotationFlags = ui::Transform::RotationFlags;
+
     enum class CaptureFill {CLEAR, OPAQUE};
 
     static float getCaptureFillValue(CaptureFill captureFill);
 
     RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
-               ui::Dataspace reqDataSpace,
-               ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
+               ui::Dataspace reqDataSpace, const Rect& displayViewport,
+               RotationFlags rotation = ui::Transform::ROT_0)
           : mReqWidth(reqWidth),
             mReqHeight(reqHeight),
             mReqDataSpace(reqDataSpace),
             mCaptureFill(captureFill),
-            mRotationFlags(rotation) {}
+            mRotationFlags(rotation),
+            mDisplayViewport(displayViewport) {}
 
     virtual ~RenderArea() = default;
 
@@ -65,27 +68,31 @@
     virtual Rect getSourceCrop() const = 0;
 
     // Returns the rotation of the source crop and the layers.
-    ui::Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
+    RotationFlags getRotationFlags() const { return mRotationFlags; }
 
     // Returns the size of the physical render area.
-    int getReqWidth() const { return mReqWidth; };
-    int getReqHeight() const { return mReqHeight; };
+    int getReqWidth() const { return static_cast<int>(mReqWidth); }
+    int getReqHeight() const { return static_cast<int>(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; };
+    CaptureFill getCaptureFill() const { return mCaptureFill; }
 
-    virtual const sp<const DisplayDevice> getDisplayDevice() const = 0;
+    virtual sp<const DisplayDevice> getDisplayDevice() const = 0;
+
+    // Returns the source display viewport.
+    const Rect& getDisplayViewport() const { return mDisplayViewport; }
 
 private:
     const uint32_t mReqWidth;
     const uint32_t mReqHeight;
     const ui::Dataspace mReqDataSpace;
     const CaptureFill mCaptureFill;
-    const ui::Transform::orientation_flags mRotationFlags;
+    const RotationFlags mRotationFlags;
+    const Rect mDisplayViewport;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 0c94052..809a0e5 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 //#define LOG_NDEBUG 0
 
@@ -64,7 +68,7 @@
     DispSyncThread(const char* name, bool showTraceDetailedInfo)
           : mName(name),
             mStop(false),
-            mModelLocked(false),
+            mModelLocked("DispSync:ModelLocked", false),
             mPeriod(0),
             mPhase(0),
             mReferenceTime(0),
@@ -121,13 +125,11 @@
     void lockModel() {
         Mutex::Autolock lock(mMutex);
         mModelLocked = true;
-        ATRACE_INT("DispSync:ModelLocked", mModelLocked);
     }
 
     void unlockModel() {
         Mutex::Autolock lock(mMutex);
         mModelLocked = false;
-        ATRACE_INT("DispSync:ModelLocked", mModelLocked);
     }
 
     virtual bool threadLoop() {
@@ -431,7 +433,7 @@
     const char* const mName;
 
     bool mStop;
-    bool mModelLocked;
+    TracedOrdinal<bool> mModelLocked;
 
     nsecs_t mPeriod;
     nsecs_t mPhase;
@@ -454,33 +456,24 @@
 
 class ZeroPhaseTracer : public DispSync::Callback {
 public:
-    ZeroPhaseTracer() : mParity(false) {}
+    ZeroPhaseTracer() : mParity("ZERO_PHASE_VSYNC", false) {}
 
     virtual void onDispSyncEvent(nsecs_t /*when*/) {
         mParity = !mParity;
-        ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0);
     }
 
 private:
-    bool mParity;
+    TracedOrdinal<bool> mParity;
 };
 
-DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) {
+DispSync::DispSync(const char* name, bool hasSyncFramework)
+      : mName(name), mIgnorePresentFences(!hasSyncFramework) {
     // 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() {
-    mThread->stop();
-    mThread->requestExitAndWait();
-}
-
-void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
-    mIgnorePresentFences = !hasSyncFramework;
-    mPresentTimeOffset = dispSyncPresentTimeOffset;
     mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
 
     // set DispSync to SCHED_FIFO to minimize jitter
@@ -498,6 +491,11 @@
     }
 }
 
+DispSync::~DispSync() {
+    mThread->stop();
+    mThread->requestExitAndWait();
+}
+
 void DispSync::reset() {
     Mutex::Autolock lock(mMutex);
     resetLocked();
@@ -544,7 +542,8 @@
     resetLocked();
 }
 
-bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+bool DispSync::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> /*hwcVsyncPeriod*/,
+                               bool* periodFlushed) {
     Mutex::Autolock lock(mMutex);
 
     ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp));
@@ -623,13 +622,6 @@
     return mThread->addEventListener(name, phase, callback, lastCallbackTime);
 }
 
-void DispSync::setRefreshSkipCount(int count) {
-    Mutex::Autolock lock(mMutex);
-    ALOGD("setRefreshSkipCount(%d)", count);
-    mRefreshSkipCount = count;
-    updateModelLocked();
-}
-
 status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) {
     Mutex::Autolock lock(mMutex);
     return mThread->removeEventListener(callback, outLastCallbackTime);
@@ -712,9 +704,6 @@
             ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase));
         }
 
-        // Artificially inflate the period if requested.
-        mPeriod += mPeriod * mRefreshSkipCount;
-
         mThread->updateModel(mPeriod, mPhase, mReferenceTime);
         mModelUpdated = true;
     }
@@ -725,10 +714,6 @@
         return;
     }
 
-    // Need to compare present fences against the un-adjusted refresh period,
-    // since they might arrive between two events.
-    nsecs_t period = mPeriod / (1 + mRefreshSkipCount);
-
     int numErrSamples = 0;
     nsecs_t sqErrSum = 0;
 
@@ -747,9 +732,9 @@
             continue;
         }
 
-        nsecs_t sampleErr = (sample - mPhase) % period;
-        if (sampleErr > period / 2) {
-            sampleErr -= period;
+        nsecs_t sampleErr = (sample - mPhase) % mPeriod;
+        if (sampleErr > mPeriod / 2) {
+            sampleErr -= mPeriod;
         }
         sqErrSum += sampleErr * sampleErr;
         numErrSamples++;
@@ -804,8 +789,7 @@
 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, "mPeriod: %" PRId64 " ns (%.3f fps)\n", mPeriod, 1000000000.0 / mPeriod);
     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",
@@ -868,3 +852,6 @@
 } // namespace impl
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 3e33c7e..2d9afc9 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -49,11 +49,11 @@
     virtual void reset() = 0;
     virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0;
     virtual void beginResync() = 0;
-    virtual bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) = 0;
+    virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                 bool* periodFlushed) = 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,
                                       nsecs_t lastCallbackTime) = 0;
     virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0;
@@ -88,11 +88,10 @@
 // needed.
 class DispSync : public android::DispSync {
 public:
-    explicit DispSync(const char* name);
+    // hasSyncFramework specifies whether the platform supports present fences.
+    DispSync(const char* name, bool hasSyncFramework);
     ~DispSync() override;
 
-    void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset);
-
     // reset clears the resync samples and error value.
     void reset() override;
 
@@ -127,7 +126,8 @@
     // down the DispSync model, and false otherwise.
     // periodFlushed will be set to true if mPendingPeriod is flushed to
     // mIntendedPeriod, and false otherwise.
-    bool addResyncSample(nsecs_t timestamp, bool* periodFlushed) override;
+    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed) override;
     void endResync() override;
 
     // The setPeriod method sets the vsync event model's period to a specific
@@ -138,12 +138,6 @@
     // The getPeriod method returns the current vsync period.
     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) 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
@@ -252,18 +246,12 @@
     std::shared_ptr<FenceTime> mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE};
     size_t mPresentSampleOffset;
 
-    int mRefreshSkipCount;
-
     // mThread is the thread from which all the callbacks are called.
     sp<DispSyncThread> mThread;
 
     // mMutex is used to protect access to all member variables.
     mutable Mutex mMutex;
 
-    // This is the offset from the present fence timestamps to the corresponding
-    // vsync event.
-    int64_t mPresentTimeOffset;
-
     // Ignore present (retire) fences if the device doesn't have support for the
     // sync framework
     bool mIgnorePresentFences;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 5faf46e..776e984 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "DispSyncSource.h"
@@ -27,23 +31,18 @@
 
 namespace android {
 
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset,
-                               nsecs_t offsetThresholdForNextVsync, bool traceVsync,
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
                                const char* name)
       : mName(name),
+        mValue(base::StringPrintf("VSYNC-%s", name), 0),
         mTraceVsync(traceVsync),
         mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
-        mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)),
-        mVsyncOffsetLabel(base::StringPrintf("VsyncOffset-%s", name)),
-        mVsyncNegativeOffsetLabel(base::StringPrintf("VsyncNegativeOffset-%s", name)),
         mDispSync(dispSync),
-        mPhaseOffset(phaseOffset),
-        mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
+        mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
 
 void DispSyncSource::setVSyncEnabled(bool enable) {
     std::lock_guard lock(mVsyncMutex);
     if (enable) {
-        tracePhaseOffset();
         status_t err = mDispSync->addEventListener(mName, mPhaseOffset,
                                                    static_cast<DispSync::Callback*>(this),
                                                    mLastCallbackTime);
@@ -70,10 +69,6 @@
 void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
     std::lock_guard lock(mVsyncMutex);
     const nsecs_t period = mDispSync->getPeriod();
-    // Check if offset should be handled as negative
-    if (phaseOffset >= mOffsetThresholdForNextVsync) {
-        phaseOffset -= period;
-    }
 
     // Normalize phaseOffset to [-period, period)
     const int numPeriods = phaseOffset / period;
@@ -83,7 +78,6 @@
     }
 
     mPhaseOffset = phaseOffset;
-    tracePhaseOffset();
 
     // If we're not enabled, we don't need to mess with the listeners
     if (!mEnabled) {
@@ -106,7 +100,6 @@
 
     if (mTraceVsync) {
         mValue = (mValue + 1) % 2;
-        ATRACE_INT(mVsyncEventLabel.c_str(), mValue);
     }
 
     if (callback != nullptr) {
@@ -114,14 +107,7 @@
     }
 }
 
-void DispSyncSource::tracePhaseOffset() {
-    if (mPhaseOffset > 0) {
-        ATRACE_INT(mVsyncOffsetLabel.c_str(), mPhaseOffset);
-        ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), 0);
-    } else {
-        ATRACE_INT(mVsyncOffsetLabel.c_str(), 0);
-        ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), -mPhaseOffset);
-    }
-}
-
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 50560a5..328b8c1 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -20,17 +20,18 @@
 
 #include "DispSync.h"
 #include "EventThread.h"
+#include "TracedOrdinal.h"
 
 namespace android {
 
 class DispSyncSource final : public VSyncSource, private DispSync::Callback {
 public:
-    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync,
-                   bool traceVsync, const char* name);
+    DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
 
     ~DispSyncSource() override = default;
 
     // The following methods are implementation of VSyncSource.
+    const char* getName() const override { return mName; }
     void setVSyncEnabled(bool enable) override;
     void setCallback(VSyncSource::Callback* callback) override;
     void setPhaseOffset(nsecs_t phaseOffset) override;
@@ -39,16 +40,11 @@
     // The following method is the implementation of the DispSync::Callback.
     virtual void onDispSyncEvent(nsecs_t when);
 
-    void tracePhaseOffset() REQUIRES(mVsyncMutex);
-
     const char* const mName;
-    int mValue = 0;
+    TracedOrdinal<int> mValue;
 
     const bool mTraceVsync;
     const std::string mVsyncOnLabel;
-    const std::string mVsyncEventLabel;
-    const std::string mVsyncOffsetLabel;
-    const std::string mVsyncNegativeOffsetLabel;
     nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0;
 
     DispSync* mDispSync;
@@ -57,8 +53,7 @@
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 
     std::mutex mVsyncMutex;
-    nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex);
-    const nsecs_t mOffsetThresholdForNextVsync;
+    TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
     bool mEnabled GUARDED_BY(mVsyncMutex) = false;
 };
 
diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp
index fb6cff5..7f9db9c 100644
--- a/services/surfaceflinger/Scheduler/EventControlThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventControlThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <pthread.h>
 #include <sched.h>
 #include <sys/resource.h>
@@ -31,7 +35,7 @@
 namespace impl {
 
 EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
-      : mSetVSyncEnabled(function) {
+      : mSetVSyncEnabled(std::move(function)) {
     pthread_setname_np(mThread.native_handle(), "EventControlThread");
 
     pid_t tid = pthread_gettid_np(mThread.native_handle());
@@ -73,3 +77,6 @@
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 9d1f777..acab5a6 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include <pthread.h>
@@ -36,6 +40,7 @@
 #include <utils/Trace.h>
 
 #include "EventThread.h"
+#include "HwcStrongTypes.h"
 
 using namespace std::chrono_literals;
 
@@ -101,10 +106,12 @@
     return event;
 }
 
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
+                                              HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
     DisplayEventReceiver::Event event;
     event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
-    event.config.configId = configId;
+    event.config.configId = configId.value();
+    event.config.vsyncPeriod = vsyncPeriod;
     return event;
 }
 
@@ -114,7 +121,7 @@
                                              ResyncCallback resyncCallback,
                                              ISurfaceComposer::ConfigChanged configChanged)
       : resyncCallback(std::move(resyncCallback)),
-        configChanged(configChanged),
+        mConfigChanged(configChanged),
         mEventThread(eventThread),
         mChannel(gui::BitTube::DefaultSize) {}
 
@@ -143,6 +150,18 @@
     mEventThread->requestNextVsync(this);
 }
 
+void EventThreadConnection::toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) {
+    ATRACE_NAME("enableConfigEvents");
+    mConfigChanged = configChangeFlag;
+
+    // In principle it's possible for rapidly toggling config events to drop an
+    // event here, but it's unlikely in practice.
+    if (configChangeFlag == ISurfaceComposer::eConfigChangedDispatch) {
+        mForcedConfigChangeDispatch = true;
+        mEventThread->requestLatestConfig();
+    }
+}
+
 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);
@@ -154,23 +173,11 @@
 
 namespace impl {
 
-EventThread::EventThread(std::unique_ptr<VSyncSource> src,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : EventThread(nullptr, std::move(src), std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback,
-                         const char* threadName)
-      : EventThread(src, nullptr, std::move(interceptVSyncsCallback), threadName) {}
-
-EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc,
-                         InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName)
-      : mVSyncSource(src),
-        mVSyncSourceUnique(std::move(uniqueSrc)),
+EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource,
+                         InterceptVSyncsCallback interceptVSyncsCallback)
+      : mVSyncSource(std::move(vsyncSource)),
         mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)),
-        mThreadName(threadName) {
-    if (src == nullptr) {
-        mVSyncSource = mVSyncSourceUnique.get();
-    }
+        mThreadName(mVSyncSource->getName()) {
     mVSyncSource->setCallback(this);
 
     mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS {
@@ -178,7 +185,7 @@
         threadMain(lock);
     });
 
-    pthread_setname_np(mThread.native_handle(), threadName);
+    pthread_setname_np(mThread.native_handle(), mThreadName);
 
     pid_t tid = pthread_gettid_np(mThread.native_handle());
 
@@ -267,6 +274,24 @@
     }
 }
 
+void EventThread::requestLatestConfig() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    auto pendingConfigChange =
+            std::find_if(std::begin(mPendingEvents), std::end(mPendingEvents),
+                         [&](const DisplayEventReceiver::Event& event) {
+                             return event.header.type ==
+                                     DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED;
+                         });
+
+    // If we didn't find a pending config change event, then push out the
+    // latest one we've ever seen.
+    if (pendingConfigChange == std::end(mPendingEvents)) {
+        mPendingEvents.push_back(mLastConfigChangeEvent);
+    }
+
+    mCondition.notify_all();
+}
+
 void EventThread::onScreenReleased() {
     std::lock_guard<std::mutex> lock(mMutex);
     if (!mVSyncState || mVSyncState->synthetic) {
@@ -302,13 +327,19 @@
     mCondition.notify_all();
 }
 
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                                  nsecs_t vsyncPeriod) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    mPendingEvents.push_back(makeConfigChanged(displayId, configId));
+    mPendingEvents.push_back(makeConfigChanged(displayId, configId, vsyncPeriod));
     mCondition.notify_all();
 }
 
+size_t EventThread::getEventThreadConnectionCount() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    return mDisplayEventConnections.size();
+}
+
 void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
     DisplayEventConsumers consumers;
 
@@ -335,6 +366,9 @@
                         mInterceptVSyncsCallback(event->header.timestamp);
                     }
                     break;
+                case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+                    mLastConfigChangeEvent = *event;
+                    break;
             }
         }
 
@@ -408,8 +442,11 @@
         case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
             return true;
 
-        case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
-            return connection->configChanged == ISurfaceComposer::eConfigChangedDispatch;
+        case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: {
+            const bool forcedDispatch = connection->mForcedConfigChangeDispatch.exchange(false);
+            return forcedDispatch ||
+                    connection->mConfigChanged == ISurfaceComposer::eConfigChangedDispatch;
+        }
 
         case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
             switch (connection->vsyncRequest) {
@@ -489,3 +526,6 @@
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index dd23b88..466234d 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -16,7 +16,12 @@
 
 #pragma once
 
+#include <android-base/thread_annotations.h>
+#include <gui/DisplayEventReceiver.h>
+#include <gui/IDisplayEventConnection.h>
+#include <private/gui/BitTube.h>
 #include <sys/types.h>
+#include <utils/Errors.h>
 
 #include <condition_variable>
 #include <cstdint>
@@ -26,13 +31,7 @@
 #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>
+#include "HwcStrongTypes.h"
 
 // ---------------------------------------------------------------------------
 namespace android {
@@ -62,6 +61,8 @@
     };
 
     virtual ~VSyncSource() {}
+
+    virtual const char* getName() const = 0;
     virtual void setVSyncEnabled(bool enable) = 0;
     virtual void setCallback(Callback* callback) = 0;
     virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
@@ -78,12 +79,19 @@
     status_t stealReceiveChannel(gui::BitTube* outChannel) override;
     status_t setVsyncRate(uint32_t rate) override;
     void requestNextVsync() override; // asynchronous
+    void toggleConfigEvents(ISurfaceComposer::ConfigChanged configChangeFlag) override;
 
     // Called in response to requestNextVsync.
     const ResyncCallback resyncCallback;
 
     VSyncRequest vsyncRequest = VSyncRequest::None;
-    const ISurfaceComposer::ConfigChanged configChanged;
+    std::atomic<ISurfaceComposer::ConfigChanged> mConfigChanged =
+            ISurfaceComposer::ConfigChanged::eConfigChangedSuppress;
+    // Store whether we need to force dispatching a config change separately -
+    // if mConfigChanged ever changes before the config change is dispatched
+    // then we still need to propagate an initial config to the app if we
+    // haven't already.
+    std::atomic<bool> mForcedConfigChangeDispatch = false;
 
 private:
     virtual void onFirstRef();
@@ -107,7 +115,8 @@
     virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
 
     // called when SF changes the active config and apps needs to be notified about the change
-    virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0;
+    virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                                 nsecs_t vsyncPeriod) = 0;
 
     virtual void dump(std::string& result) const = 0;
 
@@ -118,6 +127,14 @@
     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) = 0;
+
+    // Dispatches the most recent configuration
+    // Usage of this method assumes that only the primary internal display
+    // supports multiple display configurations.
+    virtual void requestLatestConfig() = 0;
+
+    // Retrieves the number of event connections tracked by this EventThread.
+    virtual size_t getEventThreadConnectionCount() = 0;
 };
 
 namespace impl {
@@ -126,9 +143,7 @@
 public:
     using InterceptVSyncsCallback = std::function<void(nsecs_t)>;
 
-    // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete.
-    EventThread(VSyncSource*, InterceptVSyncsCallback, const char* threadName);
-    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName);
+    EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback);
     ~EventThread();
 
     sp<EventThreadConnection> createEventConnection(
@@ -137,6 +152,7 @@
     status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
     void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
     void requestNextVsync(const sp<EventThreadConnection>& connection) override;
+    void requestLatestConfig() override;
 
     // called before the screen is turned off from main thread
     void onScreenReleased() override;
@@ -146,21 +162,20 @@
 
     void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
 
-    void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override;
+    void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId,
+                         nsecs_t vsyncPeriod) override;
 
     void dump(std::string& result) const override;
 
     void setPhaseOffset(nsecs_t phaseOffset) override;
 
+    size_t getEventThreadConnectionCount() override;
+
 private:
     friend EventThreadTest;
 
     using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>;
 
-    // TODO(b/128863962): 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,
@@ -174,9 +189,7 @@
     // Implements VSyncSource::Callback
     void onVSyncEvent(nsecs_t timestamp) override;
 
-    // TODO(b/128863962): 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 std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
 
     const InterceptVSyncsCallback mInterceptVSyncsCallback;
     const char* const mThreadName;
@@ -187,6 +200,7 @@
 
     std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex);
     std::deque<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex);
+    DisplayEventReceiver::Event mLastConfigChangeEvent GUARDED_BY(mMutex);
 
     // VSYNC state of connected display.
     struct VSyncState {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
similarity index 63%
copy from services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
copy to services/surfaceflinger/Scheduler/HwcStrongTypes.h
index db3312b..cfbbdfe 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerCreationArgs.h
+++ b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
@@ -16,20 +16,12 @@
 
 #pragma once
 
-#include <utils/RefBase.h>
+#include "StrongTyping.h"
 
-namespace android::compositionengine {
+namespace android {
 
-class CompositionEngine;
-class LayerFE;
+// Strong types for the different indexes as they are referring to a different base.
+using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
+using HwcConfigGroupType = StrongTyping<int, struct HwcConfigGroupTypeTag, Compare>;
 
-/**
- * A parameter object for creating Layer instances
- */
-struct LayerCreationArgs {
-    // A weak pointer to the front-end layer instance that the new layer will
-    // represent.
-    wp<LayerFE> layerFE;
-};
-
-} // namespace android::compositionengine
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 90609af..fa46e6f 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -42,13 +42,13 @@
         }
     }
 
+    const char* getName() const override { return "inject"; }
     void setVSyncEnabled(bool) override {}
     void setPhaseOffset(nsecs_t) override {}
-    void pauseVsyncCallback(bool) {}
 
 private:
     std::mutex mCallbackMutex;
     VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
 };
 
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index f80c233..b313777 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -14,179 +14,169 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "LayerHistory"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "LayerHistory.h"
 
-#include <cinttypes>
-#include <cstdint>
-#include <limits>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "../Layer.h"
+#include "LayerInfo.h"
 #include "SchedulerUtils.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler::impl {
 
-std::atomic<int64_t> LayerHistory::sNextId = 0;
+namespace {
 
-LayerHistory::LayerHistory() {
+bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
+    if (layer.getFrameRate().rate > 0) {
+        return layer.isVisible();
+    }
+    return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+}
+
+bool traceEnabled() {
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.layer_history_trace", value, "0");
-    mTraceEnabled = bool(atoi(value));
+    return atoi(value);
 }
 
+bool useFrameRatePriority() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.use_frame_rate_priority", value, "1");
+    return atoi(value);
+}
+
+void trace(const wp<Layer>& weak, int fps) {
+    const auto layer = weak.promote();
+    if (!layer) return;
+
+    const auto& name = layer->getName();
+    const auto tag = "LFPS " + name;
+    ATRACE_INT(tag.c_str(), fps);
+    ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+}
+} // namespace
+
+LayerHistory::LayerHistory()
+      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
 LayerHistory::~LayerHistory() = default;
 
-std::unique_ptr<LayerHistory::LayerHandle> LayerHistory::createLayer(const std::string name,
-                                                                     float minRefreshRate,
-                                                                     float maxRefreshRate) {
-    const int64_t id = sNextId++;
-
+void LayerHistory::registerLayer(Layer* layer, float lowRefreshRate, float highRefreshRate,
+                                 LayerVoteType /*type*/) {
+    auto info = std::make_unique<LayerInfo>(lowRefreshRate, highRefreshRate);
     std::lock_guard lock(mLock);
-    mInactiveLayerInfos.emplace(id,
-                                std::make_shared<LayerInfo>(name, minRefreshRate, maxRefreshRate));
-    return std::make_unique<LayerHistory::LayerHandle>(*this, id);
+    mLayerInfos.emplace_back(layer, std::move(info));
 }
 
-void LayerHistory::destroyLayer(const int64_t id) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
     std::lock_guard lock(mLock);
-    auto it = mActiveLayerInfos.find(id);
-    if (it != mActiveLayerInfos.end()) {
-        mActiveLayerInfos.erase(it);
-    }
 
-    it = mInactiveLayerInfos.find(id);
-    if (it != mInactiveLayerInfos.end()) {
-        mInactiveLayerInfos.erase(it);
+    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+                                 [layer](const auto& pair) { return pair.first == layer; });
+    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+    const auto& info = it->second;
+    info->setLastPresentTime(presentTime, now);
+
+    // Activate layer if inactive.
+    if (const auto end = activeLayers().end(); it >= end) {
+        std::iter_swap(it, end);
+        mActiveLayersEnd++;
     }
 }
 
-void LayerHistory::insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime,
-                          bool isHdr) {
-    std::shared_ptr<LayerInfo> layerInfo;
-    {
-        std::lock_guard lock(mLock);
-        auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
-        if (layerInfoIterator != mInactiveLayerInfos.end()) {
-            layerInfo = layerInfoIterator->second;
-            mInactiveLayerInfos.erase(layerInfoIterator);
-            mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
+LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
+    std::lock_guard lock(mLock);
+
+    partitionLayers(now);
+
+    LayerHistory::Summary summary;
+    for (const auto& [weakLayer, info] : activeLayers()) {
+        const bool recent = info->isRecentlyActive(now);
+        auto layer = weakLayer.promote();
+        // Only use the layer if the reference still exists.
+        if (layer || CC_UNLIKELY(mTraceEnabled)) {
+            // Check if frame rate was set on layer.
+            const auto frameRate = layer->getFrameRate();
+            if (frameRate.rate > 0.f) {
+                const auto voteType = [&]() {
+                    switch (frameRate.type) {
+                        case Layer::FrameRateCompatibility::Default:
+                            return LayerVoteType::ExplicitDefault;
+                        case Layer::FrameRateCompatibility::ExactOrMultiple:
+                            return LayerVoteType::ExplicitExactOrMultiple;
+                        case Layer::FrameRateCompatibility::NoVote:
+                            return LayerVoteType::NoVote;
+                    }
+                }();
+                summary.push_back({layer->getName(), voteType, frameRate.rate, /* weight */ 1.0f});
+            } else if (recent) {
+                summary.push_back({layer->getName(), LayerVoteType::Heuristic,
+                                   info->getRefreshRate(now),
+                                   /* weight */ 1.0f});
+            }
+
+            if (CC_UNLIKELY(mTraceEnabled)) {
+                trace(weakLayer, round<int>(frameRate.rate));
+            }
+        }
+    }
+
+    return summary;
+}
+
+void LayerHistory::partitionLayers(nsecs_t now) {
+    const nsecs_t threshold = getActiveLayerThreshold(now);
+
+    // Collect expired and inactive layers after active layers.
+    size_t i = 0;
+    while (i < mActiveLayersEnd) {
+        auto& [weak, info] = mLayerInfos[i];
+        if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+            i++;
+            continue;
+        }
+
+        if (CC_UNLIKELY(mTraceEnabled)) {
+            trace(weak, 0);
+        }
+
+        info->clearHistory();
+        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+    }
+
+    // Collect expired layers after inactive layers.
+    size_t end = mLayerInfos.size();
+    while (i < end) {
+        if (mLayerInfos[i].first.promote()) {
+            i++;
         } else {
-            layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
-            if (layerInfoIterator != mActiveLayerInfos.end()) {
-                layerInfo = layerInfoIterator->second;
-            } else {
-                ALOGW("Inserting information about layer that is not registered: %" PRId64,
-                      layerHandle->mId);
-                return;
-            }
+            std::swap(mLayerInfos[i], mLayerInfos[--end]);
         }
     }
-    layerInfo->setLastPresentTime(presentTime);
-    layerInfo->setHDRContent(isHdr);
+
+    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
 }
 
-void LayerHistory::setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible) {
-    std::shared_ptr<LayerInfo> layerInfo;
-    {
-        std::lock_guard lock(mLock);
-        auto layerInfoIterator = mInactiveLayerInfos.find(layerHandle->mId);
-        if (layerInfoIterator != mInactiveLayerInfos.end()) {
-            layerInfo = layerInfoIterator->second;
-            if (visible) {
-                mInactiveLayerInfos.erase(layerInfoIterator);
-                mActiveLayerInfos.insert({layerHandle->mId, layerInfo});
-            }
-        } else {
-            layerInfoIterator = mActiveLayerInfos.find(layerHandle->mId);
-            if (layerInfoIterator != mActiveLayerInfos.end()) {
-                layerInfo = layerInfoIterator->second;
-            } else {
-                ALOGW("Inserting information about layer that is not registered: %" PRId64,
-                      layerHandle->mId);
-                return;
-            }
-        }
-    }
-    layerInfo->setVisibility(visible);
-}
-
-std::pair<float, bool> LayerHistory::getDesiredRefreshRateAndHDR() {
-    bool isHDR = false;
-    float newRefreshRate = 0.f;
+void LayerHistory::clear() {
     std::lock_guard lock(mLock);
 
-    removeIrrelevantLayers();
-
-    // Iterate through all layers that have been recently updated, and find the max refresh rate.
-    for (const auto& [layerId, layerInfo] : mActiveLayerInfos) {
-        const float layerRefreshRate = layerInfo->getDesiredRefreshRate();
-        if (mTraceEnabled) {
-            // Store the refresh rate in traces for easy debugging.
-            std::string layerName = "LFPS " + layerInfo->getName();
-            ATRACE_INT(layerName.c_str(), std::round(layerRefreshRate));
-            ALOGD("%s: %f", layerName.c_str(), std::round(layerRefreshRate));
-        }
-        if (layerInfo->isRecentlyActive() && layerRefreshRate > newRefreshRate) {
-            newRefreshRate = layerRefreshRate;
-        }
-        isHDR |= layerInfo->getHDRContent();
-    }
-    if (mTraceEnabled) {
-        ALOGD("LayerHistory DesiredRefreshRate: %.2f", newRefreshRate);
+    for (const auto& [layer, info] : activeLayers()) {
+        info->clearHistory();
     }
 
-    return {newRefreshRate, isHDR};
+    mActiveLayersEnd = 0;
 }
+} // namespace android::scheduler::impl
 
-void LayerHistory::removeIrrelevantLayers() {
-    const int64_t obsoleteEpsilon = systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
-    // Iterator pointing to first element in map
-    auto it = mActiveLayerInfos.begin();
-    while (it != mActiveLayerInfos.end()) {
-        // If last updated was before the obsolete time, remove it.
-        // Keep HDR layer around as long as they are visible.
-        if (!it->second->isVisible() ||
-            (!it->second->getHDRContent() && it->second->getLastUpdatedTime() < obsoleteEpsilon)) {
-            // erase() function returns the iterator of the next
-            // to last deleted element.
-            if (mTraceEnabled) {
-                ALOGD("Layer %s obsolete", it->second->getName().c_str());
-                // Make sure to update systrace to indicate that the layer was erased.
-                std::string layerName = "LFPS " + it->second->getName();
-                ATRACE_INT(layerName.c_str(), 0);
-            }
-            auto id = it->first;
-            auto layerInfo = it->second;
-            layerInfo->clearHistory();
-            mInactiveLayerInfos.insert({id, layerInfo});
-            it = mActiveLayerInfos.erase(it);
-        } else {
-            ++it;
-        }
-    }
-}
-
-void LayerHistory::clearHistory() {
-    std::lock_guard lock(mLock);
-
-    auto it = mActiveLayerInfos.begin();
-    while (it != mActiveLayerInfos.end()) {
-        auto id = it->first;
-        auto layerInfo = it->second;
-        layerInfo->clearHistory();
-        mInactiveLayerInfos.insert({id, layerInfo});
-        it = mActiveLayerInfos.erase(it);
-    }
-}
-
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 5598cc1..a1ae35c 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -16,76 +16,170 @@
 
 #pragma once
 
-#include <array>
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
-#include <unordered_map>
-
+#include <android-base/thread_annotations.h>
+#include <utils/RefBase.h>
 #include <utils/Timers.h>
 
-#include "LayerInfo.h"
-#include "SchedulerUtils.h"
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+#include "RefreshRateConfigs.h"
 
 namespace android {
+
+class Layer;
+class TestableScheduler;
+
 namespace scheduler {
 
-/*
- * This class represents information about layers that are considered current. We keep an
- * unordered map between layer name and LayerInfo.
- */
+class LayerHistoryTest;
+class LayerHistoryTestV2;
+class LayerInfo;
+class LayerInfoV2;
+
 class LayerHistory {
 public:
-    // Handle for each layer we keep track of.
-    class LayerHandle {
-    public:
-        LayerHandle(LayerHistory& lh, int64_t id) : mId(id), mLayerHistory(lh) {}
-        ~LayerHandle() { mLayerHistory.destroyLayer(mId); }
+    using LayerVoteType = RefreshRateConfigs::LayerVoteType;
 
-        const int64_t mId;
+    virtual ~LayerHistory() = default;
 
-    private:
-        LayerHistory& mLayerHistory;
-    };
+    // Layers are unregistered when the weak reference expires.
+    virtual void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+                               LayerVoteType type) = 0;
 
-    LayerHistory();
-    ~LayerHistory();
+    // Sets the display size. Client is responsible for synchronization.
+    virtual void setDisplayArea(uint32_t displayArea) = 0;
 
-    // When the layer is first created, register it.
-    std::unique_ptr<LayerHandle> createLayer(const std::string name, float minRefreshRate,
-                                             float maxRefreshRate);
+    // Marks the layer as active, and records the given state to its history.
+    virtual void record(Layer*, nsecs_t presentTime, nsecs_t now) = 0;
 
-    // Method for inserting layers and their requested present time into the unordered map.
-    void insert(const std::unique_ptr<LayerHandle>& layerHandle, nsecs_t presentTime, bool isHdr);
-    // Method for setting layer visibility
-    void setVisibility(const std::unique_ptr<LayerHandle>& layerHandle, bool visible);
+    using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
 
-    // Returns the desired refresh rate, which is a max refresh rate of all the current
-    // layers. See go/content-fps-detection-in-scheduler for more information.
-    std::pair<float, bool> getDesiredRefreshRateAndHDR();
+    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+    virtual Summary summarize(nsecs_t now) = 0;
 
-    // Clears all layer history.
-    void clearHistory();
-
-    // Removes the handle and the object from the map.
-    void destroyLayer(const int64_t id);
-
-private:
-    // Removes the layers that have been idle for a given amount of time from mLayerInfos.
-    void removeIrrelevantLayers() REQUIRES(mLock);
-
-    // Information about currently active layers.
-    std::mutex mLock;
-    std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mActiveLayerInfos GUARDED_BY(mLock);
-    std::unordered_map<int64_t, std::shared_ptr<LayerInfo>> mInactiveLayerInfos GUARDED_BY(mLock);
-
-    // Each layer has it's own ID. This variable keeps track of the count.
-    static std::atomic<int64_t> sNextId;
-
-    // Flag whether to log layer FPS in systrace
-    bool mTraceEnabled = false;
+    virtual void clear() = 0;
 };
 
+namespace impl {
+// Records per-layer history of scheduling-related information (primarily present time),
+// heuristically categorizes layers as active or inactive, and summarizes stats about
+// active layers (primarily maximum refresh rate). See go/content-fps-detection-in-scheduler.
+class LayerHistory : public android::scheduler::LayerHistory {
+public:
+    LayerHistory();
+    virtual ~LayerHistory();
+
+    // Layers are unregistered when the weak reference expires.
+    void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+                       LayerVoteType type) override;
+
+    void setDisplayArea(uint32_t /*displayArea*/) override {}
+
+    // Marks the layer as active, and records the given state to its history.
+    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+
+    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+    android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override;
+
+    void clear() override;
+
+private:
+    friend class android::scheduler::LayerHistoryTest;
+    friend TestableScheduler;
+
+    using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfo>>;
+    using LayerInfos = std::vector<LayerPair>;
+
+    struct ActiveLayers {
+        LayerInfos& infos;
+        const size_t index;
+
+        auto begin() { return infos.begin(); }
+        auto end() { return begin() + static_cast<long>(index); }
+    };
+
+    ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
+
+    // Iterates over layers in a single pass, swapping pairs such that active layers precede
+    // inactive layers, and inactive layers precede expired layers. Removes expired layers by
+    // truncating after inactive layers.
+    void partitionLayers(nsecs_t now) REQUIRES(mLock);
+
+    mutable std::mutex mLock;
+
+    // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
+    // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
+    LayerInfos mLayerInfos GUARDED_BY(mLock);
+    size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+
+    // Whether to emit systrace output and debug logs.
+    const bool mTraceEnabled;
+
+    // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
+    const bool mUseFrameRatePriority;
+};
+
+class LayerHistoryV2 : public android::scheduler::LayerHistory {
+public:
+    LayerHistoryV2();
+    virtual ~LayerHistoryV2();
+
+    // Layers are unregistered when the weak reference expires.
+    void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate,
+                       LayerVoteType type) override;
+
+    // Sets the display size. Client is responsible for synchronization.
+    void setDisplayArea(uint32_t displayArea) override { mDisplayArea = displayArea; }
+
+    // Marks the layer as active, and records the given state to its history.
+    void record(Layer*, nsecs_t presentTime, nsecs_t now) override;
+
+    // Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
+    android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override;
+
+    void clear() override;
+
+private:
+    friend android::scheduler::LayerHistoryTestV2;
+    friend TestableScheduler;
+
+    using LayerPair = std::pair<wp<Layer>, std::unique_ptr<LayerInfoV2>>;
+    using LayerInfos = std::vector<LayerPair>;
+
+    struct ActiveLayers {
+        LayerInfos& infos;
+        const size_t index;
+
+        auto begin() { return infos.begin(); }
+        auto end() { return begin() + static_cast<long>(index); }
+    };
+
+    ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
+
+    // Iterates over layers in a single pass, swapping pairs such that active layers precede
+    // inactive layers, and inactive layers precede expired layers. Removes expired layers by
+    // truncating after inactive layers.
+    void partitionLayers(nsecs_t now) REQUIRES(mLock);
+
+    mutable std::mutex mLock;
+
+    // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
+    // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
+    LayerInfos mLayerInfos GUARDED_BY(mLock);
+    size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+
+    uint32_t mDisplayArea = 0;
+
+    // Whether to emit systrace output and debug logs.
+    const bool mTraceEnabled;
+
+    // Whether to use priority sent from WindowManager to determine the relevancy of the layer.
+    const bool mUseFrameRatePriority;
+};
+
+} // namespace impl
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
new file mode 100644
index 0000000..6ef6ce4
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "LayerHistoryV2"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "LayerHistory.h"
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <algorithm>
+#include <cmath>
+#include <string>
+#include <utility>
+
+#include "../Layer.h"
+#include "SchedulerUtils.h"
+
+#include "LayerInfoV2.h"
+
+namespace android::scheduler::impl {
+
+namespace {
+
+bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
+    if (layer.getFrameRate().rate > 0) {
+        return layer.isVisible();
+    }
+    return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
+}
+
+bool traceEnabled() {
+    return property_get_bool("debug.sf.layer_history_trace", false);
+}
+
+bool useFrameRatePriority() {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("debug.sf.use_frame_rate_priority", value, "1");
+    return atoi(value);
+}
+
+void trace(const wp<Layer>& weak, LayerHistory::LayerVoteType type, int fps) {
+    const auto layer = weak.promote();
+    if (!layer) return;
+
+    const auto& name = layer->getName();
+    const auto noVoteTag = "LFPS NoVote " + name;
+    const auto heuristicVoteTag = "LFPS Heuristic " + name;
+    const auto explicitDefaultVoteTag = "LFPS ExplicitDefault" + name;
+    const auto explicitExactOrMultipleVoteTag = "LFPS ExplicitExactOrMultiple" + name;
+    const auto minVoteTag = "LFPS Min " + name;
+    const auto maxVoteTag = "LFPS Max " + name;
+
+    ATRACE_INT(noVoteTag.c_str(), type == LayerHistory::LayerVoteType::NoVote ? 1 : 0);
+    ATRACE_INT(heuristicVoteTag.c_str(), type == LayerHistory::LayerVoteType::Heuristic ? fps : 0);
+    ATRACE_INT(explicitDefaultVoteTag.c_str(),
+               type == LayerHistory::LayerVoteType::ExplicitDefault ? fps : 0);
+    ATRACE_INT(explicitExactOrMultipleVoteTag.c_str(),
+               type == LayerHistory::LayerVoteType::ExplicitExactOrMultiple ? fps : 0);
+    ATRACE_INT(minVoteTag.c_str(), type == LayerHistory::LayerVoteType::Min ? 1 : 0);
+    ATRACE_INT(maxVoteTag.c_str(), type == LayerHistory::LayerVoteType::Max ? 1 : 0);
+
+    ALOGD("%s: %s @ %d Hz", __FUNCTION__, name.c_str(), fps);
+}
+} // namespace
+
+LayerHistoryV2::LayerHistoryV2()
+      : mTraceEnabled(traceEnabled()), mUseFrameRatePriority(useFrameRatePriority()) {}
+LayerHistoryV2::~LayerHistoryV2() = default;
+
+void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
+                                   LayerVoteType type) {
+    const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
+    auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type);
+    std::lock_guard lock(mLock);
+    mLayerInfos.emplace_back(layer, std::move(info));
+}
+
+void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
+    std::lock_guard lock(mLock);
+
+    const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
+                                 [layer](const auto& pair) { return pair.first == layer; });
+    LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
+
+    const auto& info = it->second;
+    info->setLastPresentTime(presentTime, now);
+
+    // Activate layer if inactive.
+    if (const auto end = activeLayers().end(); it >= end) {
+        std::iter_swap(it, end);
+        mActiveLayersEnd++;
+    }
+}
+
+LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
+    LayerHistory::Summary summary;
+
+    std::lock_guard lock(mLock);
+
+    partitionLayers(now);
+
+    for (const auto& [layer, info] : activeLayers()) {
+        const auto strong = layer.promote();
+        if (!strong) {
+            continue;
+        }
+
+        // TODO(b/144307188): This needs to be plugged into layer summary as
+        //  an additional parameter.
+        ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
+
+        const bool recent = info->isRecentlyActive(now);
+        if (recent) {
+            const auto [type, refreshRate] = info->getRefreshRate(now);
+            // Skip NoVote layer as those don't have any requirements
+            if (type == LayerHistory::LayerVoteType::NoVote) {
+                continue;
+            }
+
+            // Compute the layer's position on the screen
+            const Rect bounds = Rect(strong->getBounds());
+            const ui::Transform transform = strong->getTransform();
+            constexpr bool roundOutwards = true;
+            Rect transformed = transform.transform(bounds, roundOutwards);
+
+            const float layerArea = transformed.getWidth() * transformed.getHeight();
+            float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
+            summary.push_back({strong->getName(), type, refreshRate, weight});
+
+            if (CC_UNLIKELY(mTraceEnabled)) {
+                trace(layer, type, static_cast<int>(std::round(refreshRate)));
+            }
+        } else if (CC_UNLIKELY(mTraceEnabled)) {
+            trace(layer, LayerHistory::LayerVoteType::NoVote, 0);
+        }
+    }
+
+    return summary;
+}
+
+void LayerHistoryV2::partitionLayers(nsecs_t now) {
+    const nsecs_t threshold = getActiveLayerThreshold(now);
+
+    // Collect expired and inactive layers after active layers.
+    size_t i = 0;
+    while (i < mActiveLayersEnd) {
+        auto& [weak, info] = mLayerInfos[i];
+        if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
+            i++;
+            // Set layer vote if set
+            const auto frameRate = layer->getFrameRate();
+            const auto voteType = [&]() {
+                switch (frameRate.type) {
+                    case Layer::FrameRateCompatibility::Default:
+                        return LayerVoteType::ExplicitDefault;
+                    case Layer::FrameRateCompatibility::ExactOrMultiple:
+                        return LayerVoteType::ExplicitExactOrMultiple;
+                    case Layer::FrameRateCompatibility::NoVote:
+                        return LayerVoteType::NoVote;
+                }
+            }();
+            if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
+                info->setLayerVote(voteType, frameRate.rate);
+            } else {
+                info->resetLayerVote();
+            }
+            continue;
+        }
+
+        if (CC_UNLIKELY(mTraceEnabled)) {
+            trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
+        }
+
+        info->clearHistory();
+        std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
+    }
+
+    // Collect expired layers after inactive layers.
+    size_t end = mLayerInfos.size();
+    while (i < end) {
+        if (mLayerInfos[i].first.promote()) {
+            i++;
+        } else {
+            std::swap(mLayerInfos[i], mLayerInfos[--end]);
+        }
+    }
+
+    mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
+}
+
+void LayerHistoryV2::clear() {
+    std::lock_guard lock(mLock);
+
+    for (const auto& [layer, info] : activeLayers()) {
+        info->clearHistory();
+    }
+
+    mActiveLayersEnd = 0;
+}
+} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 723d71f..6d9dd43 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -16,27 +16,19 @@
 
 #include "LayerInfo.h"
 
-#include <cinttypes>
-#include <cstdint>
-#include <numeric>
-#include <string>
+#include <algorithm>
+#include <utility>
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
-LayerInfo::LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate)
-      : mName(name),
-        mMinRefreshDuration(1e9f / maxRefreshRate),
-        mLowActivityRefreshDuration(1e9f / minRefreshRate),
-        mRefreshRateHistory(mMinRefreshDuration) {}
+LayerInfo::LayerInfo(float lowRefreshRate, float highRefreshRate)
+      : mLowRefreshRate(lowRefreshRate), mHighRefreshRate(highRefreshRate) {}
 
-LayerInfo::~LayerInfo() = default;
-
-void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime) {
-    std::lock_guard lock(mLock);
+void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
 
     // Buffers can come with a present time far in the future. That keeps them relevant.
-    mLastUpdatedTime = std::max(lastPresentTime, systemTime());
+    mLastUpdatedTime = std::max(lastPresentTime, now);
     mPresentTimeHistory.insertPresentTime(mLastUpdatedTime);
 
     if (mLastPresentTime == 0) {
@@ -45,14 +37,13 @@
         return;
     }
 
-    const nsecs_t timeDiff = lastPresentTime - mLastPresentTime;
+    const nsecs_t period = lastPresentTime - mLastPresentTime;
     mLastPresentTime = lastPresentTime;
     // Ignore time diff that are too high - those are stale values
-    if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return;
-    const nsecs_t refreshDuration = std::max(timeDiff, mMinRefreshDuration);
-    const int fps = 1e9f / refreshDuration;
+    if (period > MAX_ACTIVE_LAYER_PERIOD_NS.count()) return;
+
+    const float fps = std::min(1e9f / period, mHighRefreshRate);
     mRefreshRateHistory.insertRefreshRate(fps);
 }
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 17afdda..cb81ca2 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,27 +16,37 @@
 
 #pragma once
 
-#include <cinttypes>
-#include <cstdint>
-#include <deque>
-#include <mutex>
-#include <numeric>
-#include <string>
-
-#include <log/log.h>
-
-#include <utils/Mutex.h>
 #include <utils/Timers.h>
 
+#include <chrono>
+#include <deque>
+
 #include "SchedulerUtils.h"
 
 namespace android {
+
+class Layer;
+
 namespace scheduler {
 
-/*
- * This class represents information about individial layers.
- */
+using namespace std::chrono_literals;
+
+// Maximum period between presents for a layer to be considered active.
+constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
+
+// Earliest present time for a layer to be considered active.
+constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
+    return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
+}
+
+// Stores history of present times and refresh rates for a layer.
 class LayerInfo {
+    // Layer is considered frequent if the earliest value in the window of most recent present times
+    // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
+    // favor of a low refresh rate.
+    static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
+    static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
+
     /**
      * Struct that keeps the information about the refresh rate for last
      * HISTORY_SIZE frames. This is used to better determine the refresh rate
@@ -44,9 +54,9 @@
      */
     class RefreshRateHistory {
     public:
-        explicit RefreshRateHistory(nsecs_t minRefreshDuration)
-              : mMinRefreshDuration(minRefreshDuration) {}
-        void insertRefreshRate(int refreshRate) {
+        explicit RefreshRateHistory(float highRefreshRate) : mHighRefreshRate(highRefreshRate) {}
+
+        void insertRefreshRate(float refreshRate) {
             mElements.push_back(refreshRate);
             if (mElements.size() > HISTORY_SIZE) {
                 mElements.pop_front();
@@ -54,19 +64,16 @@
         }
 
         float getRefreshRateAvg() const {
-            if (mElements.empty()) {
-                return 1e9f / mMinRefreshDuration;
-            }
-
-            return scheduler::calculate_mean(mElements);
+            return mElements.empty() ? mHighRefreshRate : calculate_mean(mElements);
         }
 
         void clearHistory() { mElements.clear(); }
 
     private:
-        std::deque<nsecs_t> mElements;
+        const float mHighRefreshRate;
+
         static constexpr size_t HISTORY_SIZE = 30;
-        const nsecs_t mMinRefreshDuration;
+        std::deque<float> mElements;
     };
 
     /**
@@ -76,6 +83,8 @@
      */
     class PresentTimeHistory {
     public:
+        static constexpr size_t HISTORY_SIZE = 90;
+
         void insertPresentTime(nsecs_t presentTime) {
             mElements.push_back(presentTime);
             if (mElements.size() > HISTORY_SIZE) {
@@ -83,60 +92,45 @@
             }
         }
 
-        // Checks whether the present time that was inserted HISTORY_SIZE ago is within a
-        // certain threshold: TIME_EPSILON_NS.
-        bool isRelevant() const {
+        // Returns whether the earliest present time is within the active threshold.
+        bool isRecentlyActive(nsecs_t now) const {
             if (mElements.size() < 2) {
                 return false;
             }
 
             // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
-            if (mElements.size() != HISTORY_SIZE &&
-                mElements.at(mElements.size() - 1) - mElements.at(0) < HISTORY_TIME.count()) {
+            if (mElements.size() < HISTORY_SIZE &&
+                mElements.back() - mElements.front() < HISTORY_TIME.count()) {
                 return false;
             }
 
-            // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds.
-            const int64_t obsoleteEpsilon =
-                    systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
-            if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) {
-                return false;
-            }
-
-            return true;
+            return mElements.back() >= getActiveLayerThreshold(now);
         }
 
-        bool isLowActivityLayer() const {
-            // We want to make sure that we received more than two frames from the layer
-            // in order to check low activity.
-            if (mElements.size() < scheduler::LOW_ACTIVITY_BUFFERS + 1) {
+        bool isFrequent(nsecs_t now) const {
+            // Assume layer is infrequent if too few present times have been recorded.
+            if (mElements.size() < FREQUENT_LAYER_WINDOW_SIZE) {
                 return false;
             }
 
-            const int64_t obsoleteEpsilon =
-                    systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count();
-            // Check the frame before last to determine whether there is low activity.
-            // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
-            // infrequent updates.
-            if (mElements.at(mElements.size() - (scheduler::LOW_ACTIVITY_BUFFERS + 1)) <
-                obsoleteEpsilon) {
-                return true;
-            }
-
-            return false;
+            // Layer is frequent if the earliest value in the window of most recent present times is
+            // within threshold.
+            const auto it = mElements.end() - FREQUENT_LAYER_WINDOW_SIZE;
+            const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
+            return *it >= threshold;
         }
 
         void clearHistory() { mElements.clear(); }
 
     private:
         std::deque<nsecs_t> mElements;
-        static constexpr size_t HISTORY_SIZE = 90;
         static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
     };
 
+    friend class LayerHistoryTest;
+
 public:
-    LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate);
-    ~LayerInfo();
+    LayerInfo(float lowRefreshRate, float highRefreshRate);
 
     LayerInfo(const LayerInfo&) = delete;
     LayerInfo& operator=(const LayerInfo&) = delete;
@@ -144,71 +138,33 @@
     // Records the last requested oresent time. It also stores information about when
     // the layer was last updated. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    void setLastPresentTime(nsecs_t lastPresentTime);
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
 
-    void setHDRContent(bool isHdr) {
-        std::lock_guard lock(mLock);
-        mIsHDR = isHdr;
-    }
+    bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
+    bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
 
-    void setVisibility(bool visible) {
-        std::lock_guard lock(mLock);
-        mIsVisible = visible;
-    }
-
-    // Checks the present time history to see whether the layer is relevant.
-    bool isRecentlyActive() const {
-        std::lock_guard lock(mLock);
-        return mPresentTimeHistory.isRelevant();
-    }
-
-    // Calculate the average refresh rate.
-    float getDesiredRefreshRate() const {
-        std::lock_guard lock(mLock);
-
-        if (mPresentTimeHistory.isLowActivityLayer()) {
-            return 1e9f / mLowActivityRefreshDuration;
-        }
-        return mRefreshRateHistory.getRefreshRateAvg();
-    }
-
-    bool getHDRContent() {
-        std::lock_guard lock(mLock);
-        return mIsHDR;
-    }
-
-    bool isVisible() {
-        std::lock_guard lock(mLock);
-        return mIsVisible;
+    float getRefreshRate(nsecs_t now) const {
+        return isFrequent(now) ? mRefreshRateHistory.getRefreshRateAvg() : mLowRefreshRate;
     }
 
     // Return the last updated time. If the present time is farther in the future than the
     // updated time, the updated time is the present time.
-    nsecs_t getLastUpdatedTime() {
-        std::lock_guard lock(mLock);
-        return mLastUpdatedTime;
-    }
-
-    std::string getName() const { return mName; }
+    nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
 
     void clearHistory() {
-        std::lock_guard lock(mLock);
         mRefreshRateHistory.clearHistory();
         mPresentTimeHistory.clearHistory();
     }
 
 private:
-    const std::string mName;
-    const nsecs_t mMinRefreshDuration;
-    const nsecs_t mLowActivityRefreshDuration;
-    mutable std::mutex mLock;
-    nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
-    nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;
-    RefreshRateHistory mRefreshRateHistory GUARDED_BY(mLock);
-    PresentTimeHistory mPresentTimeHistory GUARDED_BY(mLock);
-    bool mIsHDR GUARDED_BY(mLock) = false;
-    bool mIsVisible GUARDED_BY(mLock) = false;
+    const float mLowRefreshRate;
+    const float mHighRefreshRate;
+
+    nsecs_t mLastUpdatedTime = 0;
+    nsecs_t mLastPresentTime = 0;
+    RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
+    PresentTimeHistory mPresentTimeHistory;
 };
 
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
new file mode 100644
index 0000000..345b8f9
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+
+#include "LayerInfoV2.h"
+
+#include <algorithm>
+#include <utility>
+
+#undef LOG_TAG
+#define LOG_TAG "LayerInfoV2"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+namespace android::scheduler {
+
+LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote)
+      : mHighRefreshRatePeriod(highRefreshRatePeriod),
+        mDefaultVote(defaultVote),
+        mLayerVote({defaultVote, 0.0f}) {}
+
+void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
+    lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
+
+    mLastUpdatedTime = std::max(lastPresentTime, now);
+
+    FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
+
+    mFrameTimes.push_back(frameTime);
+    if (mFrameTimes.size() > HISTORY_SIZE) {
+        mFrameTimes.pop_front();
+    }
+}
+
+// Returns whether the earliest present time is within the active threshold.
+bool LayerInfoV2::isRecentlyActive(nsecs_t now) const {
+    if (mFrameTimes.empty()) {
+        return false;
+    }
+
+    return mFrameTimes.back().queueTime >= getActiveLayerThreshold(now);
+}
+
+bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
+    return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                          mFrameTimeValidSince.time_since_epoch())
+                                          .count();
+}
+
+bool LayerInfoV2::isFrequent(nsecs_t now) const {
+    // If we know nothing about this layer we consider it as frequent as it might be the start
+    // of an animation.
+    if (mFrameTimes.size() < FREQUENT_LAYER_WINDOW_SIZE) {
+        return true;
+    }
+
+    // Layer is frequent if the earliest value in the window of most recent present times is
+    // within threshold.
+    const auto it = mFrameTimes.end() - FREQUENT_LAYER_WINDOW_SIZE;
+    if (!isFrameTimeValid(*it)) {
+        return true;
+    }
+
+    const nsecs_t threshold = now - MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    return it->queueTime >= threshold;
+}
+
+bool LayerInfoV2::hasEnoughDataForHeuristic() const {
+    // The layer had to publish at least HISTORY_SIZE or HISTORY_TIME of updates
+    if (mFrameTimes.size() < 2) {
+        return false;
+    }
+
+    if (!isFrameTimeValid(mFrameTimes.front())) {
+        return false;
+    }
+
+    if (mFrameTimes.size() < HISTORY_SIZE &&
+        mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
+        return false;
+    }
+
+    return true;
+}
+
+std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
+    static constexpr float MARGIN = 1.0f; // 1Hz
+
+    if (!hasEnoughDataForHeuristic()) {
+        ALOGV("Not enough data");
+        return std::nullopt;
+    }
+
+    // Calculate the refresh rate by finding the average delta between frames
+    nsecs_t totalPresentTimeDeltas = 0;
+    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+        // If there are no presentation timestamp provided we can't calculate the refresh rate
+        if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
+            return std::nullopt;
+        }
+
+        totalPresentTimeDeltas +=
+                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+    }
+    const float averageFrameTime =
+            static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
+
+    // Now once we calculated the refresh rate we need to make sure that all the frames we captured
+    // are evenly distributed and we don't calculate the average across some burst of frames.
+    for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
+        const nsecs_t presentTimeDeltas =
+                std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
+        if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
+            return std::nullopt;
+        }
+    }
+
+    const auto refreshRate = 1e9f / averageFrameTime;
+    if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
+        mLastReportedRefreshRate = refreshRate;
+    }
+
+    ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
+    return mLastReportedRefreshRate;
+}
+
+std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
+    if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
+        return {mLayerVote.type, mLayerVote.fps};
+    }
+
+    if (!isFrequent(now)) {
+        return {LayerHistory::LayerVoteType::Min, 0};
+    }
+
+    auto refreshRate = calculateRefreshRateIfPossible();
+    if (refreshRate.has_value()) {
+        return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
+    }
+
+    return {LayerHistory::LayerVoteType::Max, 0};
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.h b/services/surfaceflinger/Scheduler/LayerInfoV2.h
new file mode 100644
index 0000000..90f6310
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <utils/Timers.h>
+
+#include <chrono>
+#include <deque>
+
+#include "LayerHistory.h"
+#include "RefreshRateConfigs.h"
+#include "SchedulerUtils.h"
+
+namespace android {
+
+class Layer;
+
+namespace scheduler {
+
+using namespace std::chrono_literals;
+
+// Maximum period between presents for a layer to be considered active.
+constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms;
+
+// Earliest present time for a layer to be considered active.
+constexpr nsecs_t getActiveLayerThreshold(nsecs_t now) {
+    return now - MAX_ACTIVE_LAYER_PERIOD_NS.count();
+}
+
+// Stores history of present times and refresh rates for a layer.
+class LayerInfoV2 {
+    // Layer is considered frequent if the earliest value in the window of most recent present times
+    // is within a threshold. If a layer is infrequent, its average refresh rate is disregarded in
+    // favor of a low refresh rate.
+    static constexpr size_t FREQUENT_LAYER_WINDOW_SIZE = 3;
+    static constexpr std::chrono::nanoseconds MAX_FREQUENT_LAYER_PERIOD_NS = 250ms;
+
+    friend class LayerHistoryTestV2;
+
+public:
+    LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote);
+
+    LayerInfoV2(const LayerInfo&) = delete;
+    LayerInfoV2& operator=(const LayerInfoV2&) = delete;
+
+    // Records the last requested present time. It also stores information about when
+    // the layer was last updated. If the present time is farther in the future than the
+    // updated time, the updated time is the present time.
+    void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
+
+    bool isRecentlyActive(nsecs_t now) const;
+
+    // Sets an explicit layer vote. This usually comes directly from the application via
+    // ANativeWindow_setFrameRate API
+    void setLayerVote(LayerHistory::LayerVoteType type, float fps) { mLayerVote = {type, fps}; }
+
+    // Sets the default layer vote. This will be the layer vote after calling to resetLayerVote().
+    // This is used for layers that called to setLayerVote() and then removed the vote, so that the
+    // layer can go back to whatever vote it had before the app voted for it.
+    void setDefaultLayerVote(LayerHistory::LayerVoteType type) { mDefaultVote = type; }
+
+    // Resets the layer vote to its default.
+    void resetLayerVote() { mLayerVote = {mDefaultVote, 0.0f}; }
+
+    std::pair<LayerHistory::LayerVoteType, float> getRefreshRate(nsecs_t now);
+
+    // Return the last updated time. If the present time is farther in the future than the
+    // updated time, the updated time is the present time.
+    nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
+
+    void clearHistory() {
+        // Mark mFrameTimeValidSince to now to ignore all previous frame times.
+        // We are not deleting the old frame to keep track of whether we should treat the first
+        // buffer as Max as we don't know anything about this layer or Min as this layer is
+        // posting infrequent updates.
+        mFrameTimeValidSince = std::chrono::steady_clock::now();
+    }
+
+private:
+    // Used to store the layer timestamps
+    struct FrameTimeData {
+        nsecs_t presetTime; // desiredPresentTime, if provided
+        nsecs_t queueTime;  // buffer queue time
+    };
+
+    bool isFrequent(nsecs_t now) const;
+    bool hasEnoughDataForHeuristic() const;
+    std::optional<float> calculateRefreshRateIfPossible();
+    bool isFrameTimeValid(const FrameTimeData&) const;
+
+    // Used for sanitizing the heuristic data
+    const nsecs_t mHighRefreshRatePeriod;
+    LayerHistory::LayerVoteType mDefaultVote;
+
+    nsecs_t mLastUpdatedTime = 0;
+
+    float mLastReportedRefreshRate = 0.0f;
+
+    // Holds information about the layer vote
+    struct {
+        LayerHistory::LayerVoteType type;
+        float fps;
+    } mLayerVote;
+
+    std::deque<FrameTimeData> mFrameTimes;
+    std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
+            std::chrono::steady_clock::now();
+    static constexpr size_t HISTORY_SIZE = 90;
+    static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
+};
+
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index fcb307f..005d157 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <errno.h>
 #include <stdint.h>
 #include <sys/types.h>
@@ -85,24 +89,6 @@
     mHandler = new Handler(*this);
 }
 
-void MessageQueue::setEventThread(android::EventThread* eventThread,
-                                  ResyncCallback resyncCallback) {
-    if (mEventThread == eventThread) {
-        return;
-    }
-
-    if (mEventTube.getFd() >= 0) {
-        mLooper->removeFd(mEventTube.getFd());
-    }
-
-    mEventThread = eventThread;
-    mEvents = eventThread->createEventConnection(std::move(resyncCallback),
-                                                 ISurfaceComposer::eConfigChangedSuppress);
-    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());
@@ -177,3 +163,6 @@
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 0b2206d..fcfc4aa 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -85,8 +85,6 @@
     virtual ~MessageQueue();
 
     virtual void init(const sp<SurfaceFlinger>& flinger) = 0;
-    // TODO(b/128863962): 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;
@@ -115,7 +113,6 @@
 
     sp<SurfaceFlinger> mFlinger;
     sp<Looper> mLooper;
-    android::EventThread* mEventThread;
     sp<EventThreadConnection> mEvents;
     gui::BitTube mEventTube;
     sp<Handler> mHandler;
@@ -126,7 +123,6 @@
 public:
     ~MessageQueue() override = default;
     void init(const sp<SurfaceFlinger>& flinger) override;
-    void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override;
     void setEventConnection(const sp<EventThreadConnection>& connection) override;
 
     void waitMessage() override;
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
similarity index 84%
rename from services/surfaceflinger/Scheduler/IdleTimer.cpp
rename to services/surfaceflinger/Scheduler/OneShotTimer.cpp
index 37fdfc7..a90d05e 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -14,31 +14,32 @@
  * limitations under the License.
  */
 
-#include "IdleTimer.h"
+#include "OneShotTimer.h"
 
 #include <chrono>
+#include <sstream>
 #include <thread>
 
 namespace android {
 namespace scheduler {
 
-IdleTimer::IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
-                     const TimeoutCallback& timeoutCallback)
+OneShotTimer::OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
+                           const TimeoutCallback& timeoutCallback)
       : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {}
 
-IdleTimer::~IdleTimer() {
+OneShotTimer::~OneShotTimer() {
     stop();
 }
 
-void IdleTimer::start() {
+void OneShotTimer::start() {
     {
         std::lock_guard<std::mutex> lock(mMutex);
         mState = TimerState::RESET;
     }
-    mThread = std::thread(&IdleTimer::loop, this);
+    mThread = std::thread(&OneShotTimer::loop, this);
 }
 
-void IdleTimer::stop() {
+void OneShotTimer::stop() {
     {
         std::lock_guard<std::mutex> lock(mMutex);
         mState = TimerState::STOPPED;
@@ -49,7 +50,7 @@
     }
 }
 
-void IdleTimer::loop() {
+void OneShotTimer::loop() {
     while (true) {
         bool triggerReset = false;
         bool triggerTimeout = false;
@@ -98,9 +99,9 @@
             mTimeoutCallback();
         }
     }
-} // namespace scheduler
+}
 
-void IdleTimer::reset() {
+void OneShotTimer::reset() {
     {
         std::lock_guard<std::mutex> lock(mMutex);
         mState = TimerState::RESET;
@@ -108,5 +109,11 @@
     mCondition.notify_all();
 }
 
+std::string OneShotTimer::dump() const {
+    std::ostringstream stream;
+    stream << mInterval.count() << " ms";
+    return stream.str();
+}
+
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
similarity index 92%
rename from services/surfaceflinger/Scheduler/IdleTimer.h
rename to services/surfaceflinger/Scheduler/OneShotTimer.h
index 2646688..b005754 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -29,15 +29,15 @@
  * Class that sets off a timer for a given interval, and fires a callback when the
  * interval expires.
  */
-class IdleTimer {
+class OneShotTimer {
 public:
     using Interval = std::chrono::milliseconds;
     using ResetCallback = std::function<void()>;
     using TimeoutCallback = std::function<void()>;
 
-    IdleTimer(const Interval& interval, const ResetCallback& resetCallback,
-              const TimeoutCallback& timeoutCallback);
-    ~IdleTimer();
+    OneShotTimer(const Interval& interval, const ResetCallback& resetCallback,
+                 const TimeoutCallback& timeoutCallback);
+    ~OneShotTimer();
 
     // Initializes and turns on the idle timer.
     void start();
@@ -46,6 +46,8 @@
     // Resets the wakeup time and fires the reset callback.
     void reset();
 
+    std::string dump() const;
+
 private:
     // Enum to track in what state is the timer.
     enum class TimerState {
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 8a2604f..63d9c4b 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -18,115 +18,315 @@
 
 #include <cutils/properties.h>
 
+#include <optional>
+
 #include "SurfaceFlingerProperties.h"
 
-namespace android {
-using namespace android::sysprop;
+namespace {
 
-namespace scheduler {
+std::optional<nsecs_t> getProperty(const char* name) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get(name, value, "-1");
+    if (const int i = atoi(value); i != -1) return i;
+    return std::nullopt;
+}
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
-PhaseOffsets::~PhaseOffsets() = default;
+bool fpsEqualsWithMargin(float fpsA, float fpsB) {
+    static constexpr float MARGIN = 0.01f;
+    return std::abs(fpsA - fpsB) <= MARGIN;
+}
+
+} // namespace
+
+namespace android::scheduler {
+
+PhaseConfiguration::~PhaseConfiguration() = 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, "2000000");
-    const int highFpsLateAppOffsetNs = atoi(value);
-
-    property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000");
-    const int highFpsLateSfOffsetNs = atoi(value);
-
-    // Below defines the threshold when an offset is considered to be negative, i.e. targeting
-    // for the N+2 vsync instead of N+1. This means that:
-    // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
-    // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
-    property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1");
-    const int phaseOffsetThresholdForNextVsyncNs = atoi(value);
-
-    Offsets defaultOffsets;
-    Offsets highFpsOffsets;
-    defaultOffsets.early = {RefreshRateType::DEFAULT,
-                            earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs,
-                            earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs};
-    defaultOffsets.earlyGl = {RefreshRateType::DEFAULT,
-                              earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs,
-                              earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs};
-    defaultOffsets.late = {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs};
-
-    highFpsOffsets.early = {RefreshRateType::PERFORMANCE,
-                            highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs
-                                                         : highFpsLateSfOffsetNs,
-                            highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs
-                                                          : highFpsLateAppOffsetNs};
-    highFpsOffsets.earlyGl = {RefreshRateType::PERFORMANCE,
-                              highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs
-                                                             : highFpsLateSfOffsetNs,
-                              highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs
-                                                              : highFpsLateAppOffsetNs};
-    highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs,
-                           highFpsLateAppOffsetNs};
-
-    mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets});
-    mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
-    mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
-
-    mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1
-            ? phaseOffsetThresholdForNextVsyncNs
-            : std::numeric_limits<nsecs_t>::max();
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
-        android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const {
-    return mOffsets.at(refreshRateType);
-}
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
+        // for the N+2 vsync instead of N+1. This means that:
+        // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
+        // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
+        mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+                                       .value_or(std::numeric_limits<nsecs_t>::max())),
+        mOffsets(initializeOffsets(refreshRateConfigs)),
+        mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
 
 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);
+    using base::StringAppendF;
+    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"
+                  "next VSYNC threshold: %9" PRId64 " ns\n",
+                  late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
+                  mThresholdForNextVsync);
 }
 
-nsecs_t PhaseOffsets::getCurrentAppOffset() {
-    return getCurrentOffsets().late.app;
+std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
+        const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+    std::unordered_map<float, Offsets> offsets;
+
+    for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
+        if (refreshRate.fps > 65.0f) {
+            offsets.emplace(refreshRate.fps, getHighFpsOffsets(refreshRate.vsyncPeriod));
+        } else {
+            offsets.emplace(refreshRate.fps, getDefaultOffsets(refreshRate.vsyncPeriod));
+        }
+    }
+    return offsets;
 }
 
-nsecs_t PhaseOffsets::getCurrentSfOffset() {
-    return getCurrentOffsets().late.sf;
+PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
+    const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
+    const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
+
+    const auto earlySfOffsetNs = getProperty("debug.sf.early_phase_offset_ns");
+    const auto earlyGlSfOffsetNs = getProperty("debug.sf.early_gl_phase_offset_ns");
+    const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
+    const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
+
+    return {
+            {
+                    earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+                            : earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
+
+                    earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+            },
+            {
+                    earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+                            ? earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+                            : earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
+
+                    earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+            },
+            {
+                    sfVsyncPhaseOffsetNs < mThresholdForNextVsync
+                            ? sfVsyncPhaseOffsetNs
+                            : sfVsyncPhaseOffsetNs - vsyncDuration,
+
+                    vsyncPhaseOffsetNs,
+            },
+    };
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
+    const auto highFpsLateAppOffsetNs =
+            getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
+    const auto highFpsLateSfOffsetNs =
+            getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000);
+
+    const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns");
+    const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+    const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+    const auto highFpsEarlyGlAppOffsetNs =
+            getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+
+    return {
+            {
+                    highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
+                            ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
+                            : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+                                    vsyncDuration,
+
+                    highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+            },
+            {
+                    highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
+                                    mThresholdForNextVsync
+                            ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
+                            : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+                                    vsyncDuration,
+
+                    highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+            },
+            {
+                    highFpsLateSfOffsetNs < mThresholdForNextVsync
+                            ? highFpsLateSfOffsetNs
+                            : highFpsLateSfOffsetNs - vsyncDuration,
+
+                    highFpsLateAppOffsetNs,
+            },
+    };
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
+    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(),
+                                   [&fps](const std::pair<float, Offsets>& candidateFps) {
+                                       return fpsEqualsWithMargin(fps, candidateFps.first);
+                                   });
+    LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+    return iter->second;
+}
+
+static void validateSysprops() {
+    const auto validatePropertyBool = [](const char* prop) {
+        LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+    };
+
+    validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+    LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+                        "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+                        "duration");
+
+    LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+                        "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+                        "duration");
+
+    const auto validateProperty = [](const char* prop) {
+        LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+                            "%s is set to %" PRId64 " but expecting duration", prop,
+                            getProperty(prop).value_or(-1));
+    };
+
+    validateProperty("debug.sf.early_phase_offset_ns");
+    validateProperty("debug.sf.early_gl_phase_offset_ns");
+    validateProperty("debug.sf.early_app_phase_offset_ns");
+    validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+    validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
+    return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
+}
+
+static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
+    return sfDuration == -1 ? 1'000'000
+                            : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
+}
+
+PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const {
+    return Offsets{
+            {
+                    mSfEarlyDuration < vsyncDuration
+                            ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration)
+                            : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration,
+
+                    appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration),
+            },
+            {
+                    mSfEarlyGlDuration < vsyncDuration
+                            ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration)
+                            : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration,
+
+                    appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration),
+            },
+            {
+                    mSfDuration < vsyncDuration
+                            ? sfDurationToOffset(mSfDuration, vsyncDuration)
+                            : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration,
+
+                    appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration),
+            },
+    };
+}
+
+static std::vector<float> getRefreshRatesFromConfigs(
+        const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+    std::vector<float> refreshRates;
+    refreshRates.reserve(allRefreshRates.size());
+
+    for (const auto& [ignored, refreshRate] : allRefreshRates) {
+        refreshRates.emplace_back(refreshRate.fps);
+    }
+
+    return refreshRates;
+}
+
+std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
+        const std::vector<float>& refreshRates) const {
+    std::unordered_map<float, Offsets> offsets;
+
+    for (const auto fps : refreshRates) {
+        offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps)));
+    }
+    return offsets;
+}
+
+PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+      : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
+                       refreshRateConfigs.getCurrentRefreshRate().fps,
+                       getProperty("debug.sf.late.sf.duration").value_or(-1),
+                       getProperty("debug.sf.late.app.duration").value_or(-1),
+                       getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+                       getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+                       getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+                       getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+    validateSysprops();
+}
+
+PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
+                               nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+                               nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
+                               nsecs_t appEarlyGlDuration)
+      : mSfDuration(sfDuration),
+        mAppDuration(appDuration),
+        mSfEarlyDuration(sfEarlyDuration),
+        mAppEarlyDuration(appEarlyDuration),
+        mSfEarlyGlDuration(sfEarlyGlDuration),
+        mAppEarlyGlDuration(appEarlyGlDuration),
+        mOffsets(initializeOffsets(refreshRates)),
+        mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
+    const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) {
+        return fpsEqualsWithMargin(fps, candidateFps.first);
+    });
+
+    if (iter != mOffsets.end()) {
+        return iter->second;
+    }
+
+    // Unknown refresh rate. This might happen if we get a hotplug event for the default display.
+    // This happens only during tests and not during regular device operation.
+    // In this case just construct the offset.
+    ALOGW("Can't find offset for %.2f fps", fps);
+    return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
+}
+
+void PhaseDurations::dump(std::string& result) const {
+    const auto [early, earlyGl, late] = getCurrentOffsets();
+    using base::StringAppendF;
+    StringAppendF(&result,
+                  "           app phase:    %9" PRId64 " ns\t         SF phase:    %9" PRId64
+                  " ns\n"
+                  "           app duration: %9" PRId64 " ns\t         SF duration: %9" PRId64
+                  " ns\n"
+                  "     early app phase:    %9" PRId64 " ns\t   early SF phase:    %9" PRId64
+                  " ns\n"
+                  "     early app duration: %9" PRId64 " ns\t   early SF duration: %9" PRId64
+                  " ns\n"
+                  "  GL early app phase:    %9" PRId64 " ns\tGL early SF phase:    %9" PRId64
+                  " ns\n"
+                  "  GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
+                  " ns\n",
+                  late.app,
+
+                  late.sf,
+
+                  mAppDuration, mSfDuration,
+
+                  early.app, early.sf,
+
+                  mAppEarlyDuration, mSfEarlyDuration,
+
+                  earlyGl.app,
+
+                  earlyGl.sf,
+
+                  mAppEarlyGlDuration, mSfEarlyGlDuration);
 }
 
 } // namespace impl
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 2b5c2f1..b7d4eae 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -16,14 +16,12 @@
 
 #pragma once
 
-#include <cinttypes>
 #include <unordered_map>
 
 #include "RefreshRateConfigs.h"
 #include "VSyncModulator.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 /*
  * This class encapsulates offsets for different refresh rates. Depending
@@ -31,62 +29,100 @@
  * 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 {
+class PhaseConfiguration {
 public:
-    struct Offsets {
-        VSyncModulator::Offsets early;
-        VSyncModulator::Offsets earlyGl;
-        VSyncModulator::Offsets late;
-    };
+    using Offsets = VSyncModulator::OffsetsConfig;
 
-    virtual ~PhaseOffsets();
+    virtual ~PhaseConfiguration();
 
-    virtual nsecs_t getCurrentAppOffset() = 0;
-    virtual nsecs_t getCurrentSfOffset() = 0;
-    virtual Offsets getOffsetsForRefreshRate(
-            RefreshRateConfigs::RefreshRateType refreshRateType) const = 0;
     virtual Offsets getCurrentOffsets() const = 0;
-    virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0;
-    virtual nsecs_t getOffsetThresholdForNextVsync() const = 0;
+    virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
+
+    virtual void setRefreshRateFps(float fps) = 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;
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * PhaseDurations is the new implementation.
+ */
+class PhaseOffsets : public scheduler::PhaseConfiguration {
+public:
+    PhaseOffsets(const scheduler::RefreshRateConfigs&);
 
     // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
-    Offsets getOffsetsForRefreshRate(
-            RefreshRateConfigs::RefreshRateType refreshRateType) const override;
+    Offsets getOffsetsForRefreshRate(float fps) const override;
 
     // Returns early, early GL, and late offsets for Apps and SF.
-    Offsets getCurrentOffsets() const override {
-        return getOffsetsForRefreshRate(mRefreshRateType);
-    }
+    Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
 
     // 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;
-    }
-
-    nsecs_t getOffsetThresholdForNextVsync() const override { return mOffsetThresholdForNextVsync; }
+    void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
 
     // Returns current offsets in human friendly format.
     void dump(std::string& result) const override;
 
 private:
-    std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType =
-            RefreshRateConfigs::RefreshRateType::DEFAULT;
+    std::unordered_map<float, Offsets> initializeOffsets(
+            const scheduler::RefreshRateConfigs&) const;
+    Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
+    Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
 
-    std::unordered_map<RefreshRateConfigs::RefreshRateType, Offsets> mOffsets;
-    nsecs_t mOffsetThresholdForNextVsync;
+    const nsecs_t mThresholdForNextVsync;
+    const std::unordered_map<float, Offsets> mOffsets;
+
+    std::atomic<float> mRefreshRateFps;
 };
-} // namespace impl
 
-} // namespace scheduler
-} // namespace android
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGL)
+ * offset types.
+ */
+class PhaseDurations : public scheduler::PhaseConfiguration {
+public:
+    PhaseDurations(const scheduler::RefreshRateConfigs&);
+
+    // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+    Offsets getOffsetsForRefreshRate(float fps) const override;
+
+    // Returns early, early GL, and late offsets for Apps and SF.
+    Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
+
+    // This function should be called when the device is switching between different
+    // refresh rates, to properly update the offsets.
+    void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+    // Returns current offsets in human friendly format.
+    void dump(std::string& result) const override;
+
+protected:
+    // Used for unit tests
+    PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+                   nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+                   nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
+
+private:
+    std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const;
+    PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const;
+
+    const nsecs_t mSfDuration;
+    const nsecs_t mAppDuration;
+
+    const nsecs_t mSfEarlyDuration;
+    const nsecs_t mAppEarlyDuration;
+
+    const nsecs_t mSfEarlyGlDuration;
+    const nsecs_t mAppEarlyGlDuration;
+
+    const std::unordered_map<float, Offsets> mOffsets;
+
+    std::atomic<float> mRefreshRateFps;
+};
+
+} // namespace impl
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
new file mode 100644
index 0000000..d1de737
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -0,0 +1,389 @@
+/*
+ * 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
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "RefreshRateConfigs.h"
+#include <android-base/stringprintf.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cmath>
+
+using namespace std::chrono_literals;
+
+namespace android::scheduler {
+
+using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(
+        const std::vector<LayerRequirement>& layers) const {
+    std::lock_guard lock(mLock);
+    int contentFramerate = 0;
+    int explicitContentFramerate = 0;
+    for (const auto& layer : layers) {
+        const auto desiredRefreshRateRound = round<int>(layer.desiredRefreshRate);
+        if (layer.vote == LayerVoteType::ExplicitDefault ||
+            layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
+            if (desiredRefreshRateRound > explicitContentFramerate) {
+                explicitContentFramerate = desiredRefreshRateRound;
+            }
+        } else {
+            if (desiredRefreshRateRound > contentFramerate) {
+                contentFramerate = desiredRefreshRateRound;
+            }
+        }
+    }
+
+    if (explicitContentFramerate != 0) {
+        contentFramerate = explicitContentFramerate;
+    } else if (contentFramerate == 0) {
+        contentFramerate = round<int>(mMaxSupportedRefreshRate->fps);
+    }
+    ATRACE_INT("ContentFPS", contentFramerate);
+
+    // Find the appropriate refresh rate with minimal error
+    auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
+                            [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
+                                return std::abs(lhs->fps - contentFramerate) <
+                                        std::abs(rhs->fps - contentFramerate);
+                            });
+
+    // Some content aligns better on higher refresh rate. For example for 45fps we should choose
+    // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
+    // align well with both
+    const RefreshRate* bestSoFar = *iter;
+    constexpr float MARGIN = 0.05f;
+    float ratio = (*iter)->fps / contentFramerate;
+    if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+        while (iter != mAvailableRefreshRates.cend()) {
+            ratio = (*iter)->fps / contentFramerate;
+
+            if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+                bestSoFar = *iter;
+                break;
+            }
+            ++iter;
+        }
+    }
+
+    return *bestSoFar;
+}
+
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
+        const std::vector<LayerRequirement>& layers) const {
+    constexpr nsecs_t MARGIN = std::chrono::nanoseconds(800us).count();
+    ATRACE_CALL();
+    ALOGV("getRefreshRateForContent %zu layers", layers.size());
+
+    std::lock_guard lock(mLock);
+
+    int noVoteLayers = 0;
+    int minVoteLayers = 0;
+    int maxVoteLayers = 0;
+    int explicitDefaultVoteLayers = 0;
+    int explicitExactOrMultipleVoteLayers = 0;
+    for (const auto& layer : layers) {
+        if (layer.vote == LayerVoteType::NoVote)
+            noVoteLayers++;
+        else if (layer.vote == LayerVoteType::Min)
+            minVoteLayers++;
+        else if (layer.vote == LayerVoteType::Max)
+            maxVoteLayers++;
+        else if (layer.vote == LayerVoteType::ExplicitDefault)
+            explicitDefaultVoteLayers++;
+        else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple)
+            explicitExactOrMultipleVoteLayers++;
+    }
+
+    // Only if all layers want Min we should return Min
+    if (noVoteLayers + minVoteLayers == layers.size()) {
+        return *mAvailableRefreshRates.front();
+    }
+
+    // If we have some Max layers and no Explicit we should return Max
+    if (maxVoteLayers > 0 && explicitDefaultVoteLayers + explicitExactOrMultipleVoteLayers == 0) {
+        return *mAvailableRefreshRates.back();
+    }
+
+    // Find the best refresh rate based on score
+    std::vector<std::pair<const RefreshRate*, float>> scores;
+    scores.reserve(mAvailableRefreshRates.size());
+
+    for (const auto refreshRate : mAvailableRefreshRates) {
+        scores.emplace_back(refreshRate, 0.0f);
+    }
+
+    for (const auto& layer : layers) {
+        ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
+        if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min ||
+            layer.vote == LayerVoteType::Max) {
+            continue;
+        }
+
+        // Adjust the weight in case we have explicit layers. The priority is:
+        //  - ExplicitExactOrMultiple
+        //  - ExplicitDefault
+        //  - Heuristic
+        auto weight = layer.weight;
+        if (explicitExactOrMultipleVoteLayers + explicitDefaultVoteLayers > 0) {
+            if (layer.vote == LayerVoteType::Heuristic) {
+                weight /= 2.f;
+            }
+        }
+
+        if (explicitExactOrMultipleVoteLayers > 0) {
+            if (layer.vote == LayerVoteType::Heuristic ||
+                layer.vote == LayerVoteType::ExplicitDefault) {
+                weight /= 2.f;
+            }
+        }
+
+        for (auto& [refreshRate, overallScore] : scores) {
+            const auto displayPeriod = refreshRate->vsyncPeriod;
+            const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
+
+            // Calculate how many display vsyncs we need to present a single frame for this layer
+            auto [displayFramesQuot, displayFramesRem] = std::div(layerPeriod, displayPeriod);
+            if (displayFramesRem <= MARGIN ||
+                std::abs(displayFramesRem - displayPeriod) <= MARGIN) {
+                displayFramesQuot++;
+                displayFramesRem = 0;
+            }
+
+            float layerScore;
+            static constexpr size_t MAX_FRAMES_TO_FIT = 10; // Stop calculating when score < 0.1
+            if (displayFramesRem == 0) {
+                // Layer desired refresh rate matches the display rate.
+                layerScore = weight * 1.0f;
+            } else if (displayFramesQuot == 0) {
+                // Layer desired refresh rate is higher the display rate.
+                layerScore = weight *
+                        (static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod)) *
+                        (1.0f / (MAX_FRAMES_TO_FIT + 1));
+            } else {
+                // Layer desired refresh rate is lower the display rate. Check how well it fits the
+                // cadence
+                auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
+                int iter = 2;
+                while (diff > MARGIN && iter < MAX_FRAMES_TO_FIT) {
+                    diff = diff - (displayPeriod - diff);
+                    iter++;
+                }
+
+                layerScore = weight * (1.0f / iter);
+            }
+
+            ALOGV("%s (weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(), weight,
+                  1e9f / layerPeriod, refreshRate->name.c_str(), layerScore);
+            overallScore += layerScore;
+        }
+    }
+
+    // Now that we scored all the refresh rates we need to pick the one that got the highest score.
+    // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
+    // or the lower otherwise.
+    const RefreshRate* bestRefreshRate = maxVoteLayers > 0
+            ? getBestRefreshRate(scores.rbegin(), scores.rend())
+            : getBestRefreshRate(scores.begin(), scores.end());
+
+    return bestRefreshRate == nullptr ? *mCurrentRefreshRate : *bestRefreshRate;
+}
+
+template <typename Iter>
+const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
+    const RefreshRate* bestRefreshRate = nullptr;
+    float max = 0;
+    for (auto i = begin; i != end; ++i) {
+        const auto [refreshRate, score] = *i;
+        ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
+
+        ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
+
+        if (score > max) {
+            max = score;
+            bestRefreshRate = refreshRate;
+        }
+    }
+
+    return bestRefreshRate;
+}
+
+const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
+    return mRefreshRates;
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+    std::lock_guard lock(mLock);
+    return *mAvailableRefreshRates.front();
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+    std::lock_guard lock(mLock);
+        return *mAvailableRefreshRates.back();
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+    std::lock_guard lock(mLock);
+    return *mCurrentRefreshRate;
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
+    std::lock_guard lock(mLock);
+    if (std::find(mAvailableRefreshRates.begin(), mAvailableRefreshRates.end(),
+                  mCurrentRefreshRate) != mAvailableRefreshRates.end()) {
+        return *mCurrentRefreshRate;
+    }
+    return mRefreshRates.at(mDefaultConfig);
+}
+
+void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+    std::lock_guard lock(mLock);
+    mCurrentRefreshRate = &mRefreshRates.at(configId);
+}
+
+RefreshRateConfigs::RefreshRateConfigs(const std::vector<InputConfig>& configs,
+                                       HwcConfigIndexType currentHwcConfig) {
+    init(configs, currentHwcConfig);
+}
+
+RefreshRateConfigs::RefreshRateConfigs(
+        const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+        HwcConfigIndexType currentConfigId) {
+    std::vector<InputConfig> inputConfigs;
+    for (size_t configId = 0; configId < configs.size(); ++configId) {
+        auto configGroup = HwcConfigGroupType(configs[configId]->getConfigGroup());
+        inputConfigs.push_back({HwcConfigIndexType(static_cast<int>(configId)), configGroup,
+                                configs[configId]->getVsyncPeriod()});
+    }
+    init(inputConfigs, currentConfigId);
+}
+
+status_t RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+                                       float maxRefreshRate, bool* outPolicyChanged) {
+    std::lock_guard lock(mLock);
+    bool policyChanged = defaultConfigId != mDefaultConfig ||
+            minRefreshRate != mMinRefreshRateFps || maxRefreshRate != mMaxRefreshRateFps;
+    if (outPolicyChanged) {
+        *outPolicyChanged = policyChanged;
+    }
+    if (!policyChanged) {
+        return NO_ERROR;
+    }
+    // defaultConfigId must be a valid config ID, and within the given refresh rate range.
+    if (mRefreshRates.count(defaultConfigId) == 0) {
+        return BAD_VALUE;
+    }
+    const RefreshRate& refreshRate = mRefreshRates.at(defaultConfigId);
+    if (!refreshRate.inPolicy(minRefreshRate, maxRefreshRate)) {
+        return BAD_VALUE;
+    }
+    mDefaultConfig = defaultConfigId;
+    mMinRefreshRateFps = minRefreshRate;
+    mMaxRefreshRateFps = maxRefreshRate;
+    constructAvailableRefreshRates();
+    return NO_ERROR;
+}
+
+void RefreshRateConfigs::getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
+                                   float* maxRefreshRate) const {
+    std::lock_guard lock(mLock);
+    *defaultConfigId = mDefaultConfig;
+    *minRefreshRate = mMinRefreshRateFps;
+    *maxRefreshRate = mMaxRefreshRateFps;
+}
+
+bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
+    std::lock_guard lock(mLock);
+    for (const RefreshRate* refreshRate : mAvailableRefreshRates) {
+        if (refreshRate->configId == config) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void RefreshRateConfigs::getSortedRefreshRateList(
+        const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+        std::vector<const RefreshRate*>* outRefreshRates) {
+    outRefreshRates->clear();
+    outRefreshRates->reserve(mRefreshRates.size());
+    for (const auto& [type, refreshRate] : mRefreshRates) {
+        if (shouldAddRefreshRate(refreshRate)) {
+            ALOGV("getSortedRefreshRateList: config %d added to list policy",
+                  refreshRate.configId.value());
+            outRefreshRates->push_back(&refreshRate);
+        }
+    }
+
+    std::sort(outRefreshRates->begin(), outRefreshRates->end(),
+              [](const auto refreshRate1, const auto refreshRate2) {
+                  return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+              });
+}
+
+void RefreshRateConfigs::constructAvailableRefreshRates() {
+    // Filter configs based on current policy and sort based on vsync period
+    HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig).configGroup;
+    ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
+          mDefaultConfig.value(), group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
+    getSortedRefreshRateList(
+            [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
+                return refreshRate.configGroup == group &&
+                        refreshRate.inPolicy(mMinRefreshRateFps, mMaxRefreshRateFps);
+            },
+            &mAvailableRefreshRates);
+
+    std::string availableRefreshRates;
+    for (const auto& refreshRate : mAvailableRefreshRates) {
+        base::StringAppendF(&availableRefreshRates, "%s ", refreshRate->name.c_str());
+    }
+
+    ALOGV("Available refresh rates: %s", availableRefreshRates.c_str());
+    LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
+                        "No compatible display configs for default=%d min=%.0f max=%.0f",
+                        mDefaultConfig.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
+}
+
+// NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
+void RefreshRateConfigs::init(const std::vector<InputConfig>& configs,
+                              HwcConfigIndexType currentHwcConfig) NO_THREAD_SAFETY_ANALYSIS {
+    LOG_ALWAYS_FATAL_IF(configs.empty());
+    LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
+
+    auto buildRefreshRate = [&](InputConfig config) -> RefreshRate {
+        const float fps = 1e9f / config.vsyncPeriod;
+        return RefreshRate(config.configId, config.vsyncPeriod, config.configGroup,
+                           base::StringPrintf("%2.ffps", fps), fps);
+    };
+
+    for (const auto& config : configs) {
+        mRefreshRates.emplace(config.configId, buildRefreshRate(config));
+        if (config.configId == currentHwcConfig) {
+            mCurrentRefreshRate = &mRefreshRates.at(config.configId);
+        }
+    }
+
+    std::vector<const RefreshRate*> sortedConfigs;
+    getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+    mDefaultConfig = currentHwcConfig;
+    mMinSupportedRefreshRate = sortedConfigs.front();
+    mMaxSupportedRefreshRate = sortedConfigs.back();
+    constructAvailableRefreshRates();
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index d730058..1132a8c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -16,16 +16,25 @@
 
 #pragma once
 
+#include <android-base/stringprintf.h>
+
 #include <algorithm>
 #include <numeric>
-
-#include "android-base/stringprintf.h"
+#include <type_traits>
 
 #include "DisplayHardware/HWComposer.h"
+#include "HwcStrongTypes.h"
 #include "Scheduler/SchedulerUtils.h"
+#include "Scheduler/StrongTyping.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
+
+enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 };
+
+inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateConfigEvent rhs) {
+    using T = std::underlying_type_t<RefreshRateConfigEvent>;
+    return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
+}
 
 /**
  * This class is used to encapsulate configuration for refresh rates. It holds information
@@ -34,105 +43,178 @@
  */
 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 {
+        // The tolerance within which we consider FPS approximately equals.
+        static constexpr float FPS_EPSILON = 0.001f;
+
+        RefreshRate(HwcConfigIndexType configId, nsecs_t vsyncPeriod,
+                    HwcConfigGroupType configGroup, std::string name, float fps)
+              : configId(configId),
+                vsyncPeriod(vsyncPeriod),
+                configGroup(configGroup),
+                name(std::move(name)),
+                fps(fps) {}
         // This config ID corresponds to the position of the config in the vector that is stored
         // on the device.
-        int configId;
+        const HwcConfigIndexType configId;
+        // Vsync period in nanoseconds.
+        const nsecs_t vsyncPeriod;
+        // This configGroup for the config.
+        const HwcConfigGroupType configGroup;
         // Human readable name of the refresh rate.
-        std::string name;
-        // Refresh rate in frames per second, rounded to the nearest integer.
-        uint32_t fps = 0;
-        // config Id (returned from HWC2::Display::Config::getId())
-        hwc2_config_t id;
+        const std::string name;
+        // Refresh rate in frames per second
+        const float fps = 0;
+
+        // Checks whether the fps of this RefreshRate struct is within a given min and max refresh
+        // rate passed in. FPS_EPSILON is applied to the boundaries for approximation.
+        bool inPolicy(float minRefreshRate, float maxRefreshRate) const {
+            return (fps >= (minRefreshRate - FPS_EPSILON) && fps <= (maxRefreshRate + FPS_EPSILON));
+        }
+
+        bool operator!=(const RefreshRate& other) const {
+            return configId != other.configId || vsyncPeriod != other.vsyncPeriod ||
+                    configGroup != other.configGroup;
+        }
+
+        bool operator==(const RefreshRate& other) const { return !(*this != other); }
     };
 
-    // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
-    // baking them in.
-    const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const {
-        return mRefreshRates;
-    }
-    std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const {
-        const auto& refreshRate = mRefreshRates.find(type);
-        if (refreshRate != mRefreshRates.end()) {
-            return refreshRate->second;
-        }
-        return nullptr;
-    }
+    using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, const RefreshRate>;
 
-    RefreshRateType getRefreshRateType(hwc2_config_t id) const {
-        for (const auto& [type, refreshRate] : mRefreshRates) {
-            if (refreshRate->id == id) {
-                return type;
-            }
+    // Sets the current policy to choose refresh rates. Returns NO_ERROR if the requested policy is
+    // valid, or a negative error value otherwise. policyChanged, if non-null, will be set to true
+    // if the new policy is different from the old policy.
+    status_t setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+                       float maxRefreshRate, bool* policyChanged) EXCLUDES(mLock);
+    // Gets the current policy.
+    void getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
+                   float* maxRefreshRate) const EXCLUDES(mLock);
+
+    // Returns true if config is allowed by the current policy.
+    bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
+
+    // Describes the different options the layer voted for refresh rate
+    enum class LayerVoteType {
+        NoVote,          // Doesn't care about the refresh rate
+        Min,             // Minimal refresh rate available
+        Max,             // Maximal refresh rate available
+        Heuristic,       // Specific refresh rate that was calculated by platform using a heuristic
+        ExplicitDefault, // Specific refresh rate that was provided by the app with Default
+                         // compatibility
+        ExplicitExactOrMultiple // Specific refresh rate that was provided by the app with
+                                // ExactOrMultiple compatibility
+    };
+
+    // Captures the layer requirements for a refresh rate. This will be used to determine the
+    // display refresh rate.
+    struct LayerRequirement {
+        std::string name;         // Layer's name. Used for debugging purposes.
+        LayerVoteType vote;       // Layer vote type.
+        float desiredRefreshRate; // Layer's desired refresh rate, if applicable.
+        float weight; // Layer's weight in the range of [0, 1]. The higher the weight the more
+                      // impact this layer would have on choosing the refresh rate.
+
+        bool operator==(const LayerRequirement& other) const {
+            return name == other.name && vote == other.vote &&
+                    desiredRefreshRate == other.desiredRefreshRate && weight == other.weight;
         }
 
-        return RefreshRateType::DEFAULT;
-    }
+        bool operator!=(const LayerRequirement& other) const { return !(*this == other); }
+    };
 
-    void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) {
-        mRefreshRates.clear();
+    // Returns all available refresh rates according to the current policy.
+    const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
+            EXCLUDES(mLock);
 
-        // This is the rate that HWC encapsulates right now when the device is in DOZE mode.
-        mRefreshRates.emplace(RefreshRateType::POWER_SAVING,
-                              std::make_shared<RefreshRate>(
-                                      RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0,
-                                                  HWC2_SCREEN_OFF_CONFIG_ID}));
+    // Returns all available refresh rates according to the current policy.
+    const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers) const
+            EXCLUDES(mLock);
 
-        if (configs.size() < 1) {
-            ALOGE("Device does not have valid configs. Config size is 0.");
-            return;
-        }
+    // Returns all the refresh rates supported by the device. This won't change at runtime.
+    const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
 
-        // 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());
-        }
+    // Returns the lowest refresh rate supported by the device. This won't change at runtime.
+    const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
 
-        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;
-                  });
+    // Returns the lowest refresh rate according to the current policy. May change in runtime.
+    const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
 
-        // 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;
-            const int configId = configIdToVsyncPeriod[0].first;
-            mRefreshRates.emplace(RefreshRateType::DEFAULT,
-                                  std::make_shared<RefreshRate>(
-                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps),
-                                                      configs.at(configId)->getId()}));
-        }
+    // Returns the highest refresh rate supported by the device. This won't change at runtime.
+    const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
 
-        if (configs.size() < 2) {
-            return;
-        }
+    // Returns the highest refresh rate according to the current policy. May change in runtime.
+    const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
 
-        // 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;
-            const int configId = configIdToVsyncPeriod[1].first;
-            mRefreshRates.emplace(RefreshRateType::PERFORMANCE,
-                                  std::make_shared<RefreshRate>(
-                                          RefreshRate{configId, base::StringPrintf("%2.ffps", fps),
-                                                      static_cast<uint32_t>(fps),
-                                                      configs.at(configId)->getId()}));
-        }
-    }
+    // Returns the current refresh rate
+    const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
+
+    // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
+    // the policy.
+    const RefreshRate& getCurrentRefreshRateByPolicy() const;
+
+    // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+    // runtime.
+    const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+        return mRefreshRates.at(configId);
+    };
+
+    // Stores the current configId the device operates at
+    void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
+
+    struct InputConfig {
+        HwcConfigIndexType configId = HwcConfigIndexType(0);
+        HwcConfigGroupType configGroup = HwcConfigGroupType(0);
+        nsecs_t vsyncPeriod = 0;
+    };
+
+    RefreshRateConfigs(const std::vector<InputConfig>& configs,
+                       HwcConfigIndexType currentHwcConfig);
+    RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+                       HwcConfigIndexType currentConfigId);
 
 private:
-    std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates;
+    void init(const std::vector<InputConfig>& configs, HwcConfigIndexType currentHwcConfig);
+
+    void constructAvailableRefreshRates() REQUIRES(mLock);
+
+    void getSortedRefreshRateList(
+            const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+            std::vector<const RefreshRate*>* outRefreshRates);
+
+    // Returns the refresh rate with the highest score in the collection specified from begin
+    // to end. If there are more than one with the same highest refresh rate, the first one is
+    // returned.
+    template <typename Iter>
+    const RefreshRate* getBestRefreshRate(Iter begin, Iter end) const;
+
+    // The list of refresh rates, indexed by display config ID. This must not change after this
+    // object is initialized.
+    AllRefreshRatesMapType mRefreshRates;
+
+    // The list of refresh rates which are available in the current policy, ordered by vsyncPeriod
+    // (the first element is the lowest refresh rate)
+    std::vector<const RefreshRate*> mAvailableRefreshRates GUARDED_BY(mLock);
+
+    // The current config. This will change at runtime. This is set by SurfaceFlinger on
+    // the main thread, and read by the Scheduler (and other objects) on other threads.
+    const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
+
+    // The default config. This will change at runtime. This is set by SurfaceFlinger on
+    // the main thread, and read by the Scheduler (and other objects) on other threads.
+    HwcConfigIndexType mDefaultConfig GUARDED_BY(mLock);
+
+    // The min and max FPS allowed by the policy. This will change at runtime and set by
+    // SurfaceFlinger on the main thread.
+    float mMinRefreshRateFps GUARDED_BY(mLock) = 0;
+    float mMaxRefreshRateFps GUARDED_BY(mLock) = std::numeric_limits<float>::max();
+
+    // The min and max refresh rates supported by the device.
+    // This will not change at runtime.
+    const RefreshRate* mMinSupportedRefreshRate;
+    const RefreshRate* mMaxSupportedRefreshRate;
+
+    mutable std::mutex mLock;
 };
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 7e7c630..e44cd52 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -25,8 +25,7 @@
 #include "android-base/stringprintf.h"
 #include "utils/Timers.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 /**
  * Class to encapsulate statistics about refresh rates that the display is using. When the power
@@ -41,33 +40,30 @@
     static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR;
 
 public:
-    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats)
-          : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {}
+    RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
+                     HwcConfigIndexType currentConfigId, int currentPowerMode)
+          : mRefreshRateConfigs(refreshRateConfigs),
+            mTimeStats(timeStats),
+            mCurrentConfigMode(currentConfigId),
+            mCurrentPowerMode(currentPowerMode) {}
 
-    // 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.
+    // Sets power 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) {
+    void setConfigMode(HwcConfigIndexType configId) {
+        if (mCurrentConfigMode == configId) {
             return;
         }
         flushTime();
-        mCurrentConfigMode = mode;
+        mCurrentConfigMode = configId;
     }
 
     // Returns a map between human readable refresh rate and number of seconds the device spent in
@@ -79,57 +75,51 @@
         flushTime();
 
         std::unordered_map<std::string, int64_t> totalTime;
-        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
-            int64_t totalTimeForConfig = 0;
-            if (!config) {
-                continue;
-            }
-            if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) {
-                totalTimeForConfig = mConfigModesTotalTime.at(config->configId);
-            }
-            totalTime[config->name] = totalTimeForConfig;
+        // Multiple configs may map to the same name, e.g. "60fps". Add the
+        // times for such configs together.
+        for (const auto& [configId, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] = 0;
         }
+        for (const auto& [configId, time] : mConfigModesTotalTime) {
+            totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] += time;
+        }
+        totalTime["ScreenOff"] = mScreenOffTime;
         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() const {
-        std::ostringstream stream;
-        stream << "+  Refresh rate: running time in seconds\n";
+    void dump(std::string& result) const {
+        std::ostringstream stream("+  Refresh rate: running time in seconds\n");
+
         for (const auto& [name, time] : const_cast<RefreshRateStats*>(this)->getTotalTimes()) {
             stream << name << ": " << getDateFormatFromMs(time) << '\n';
         }
-        return stream.str();
+        result.append(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) {
+    void flushTime() {
         nsecs_t currentTime = systemTime();
         nsecs_t timeElapsed = currentTime - mPreviousRecordedTime;
         int64_t timeElapsedMs = ns2ms(timeElapsed);
         mPreviousRecordedTime = currentTime;
 
-        mConfigModesTotalTime[mode] += timeElapsedMs;
-        for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) {
-            if (!config) {
-                continue;
+        uint32_t fps = 0;
+        if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) {
+            // Normal power mode is counted under different config modes.
+            if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) {
+                mConfigModesTotalTime[mCurrentConfigMode] = 0;
             }
-            if (config->configId == mode) {
-                mTimeStats.recordRefreshRate(config->fps, timeElapsed);
-            }
+            mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs;
+            fps = static_cast<uint32_t>(std::round(
+                    mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps));
+        } else {
+            mScreenOffTime += timeElapsedMs;
         }
+        mTimeStats.recordRefreshRate(fps, timeElapsed);
     }
 
     // Formats the time in milliseconds into easy to read format.
@@ -149,13 +139,14 @@
     // Aggregate refresh rate statistics for telemetry.
     TimeStats& mTimeStats;
 
-    int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID;
-    int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF;
+    HwcConfigIndexType mCurrentConfigMode;
+    int32_t mCurrentPowerMode;
 
-    std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime;
+    std::unordered_map<HwcConfigIndexType /* configId */, int64_t /* duration in ms */>
+            mConfigModesTotalTime;
+    int64_t mScreenOffTime = 0;
 
     nsecs_t mPreviousRecordedTime = systemTime();
 };
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 8da5612..71ac90e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -14,16 +14,13 @@
  * limitations under the License.
  */
 
+#undef LOG_TAG
+#define LOG_TAG "Scheduler"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "Scheduler.h"
 
-#include <algorithm>
-#include <cinttypes>
-#include <cstdint>
-#include <memory>
-#include <numeric>
-
+#include <android-base/stringprintf.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
@@ -34,189 +31,218 @@
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <numeric>
+
+#include "../Layer.h"
 #include "DispSync.h"
 #include "DispSyncSource.h"
 #include "EventControlThread.h"
 #include "EventThread.h"
-#include "IdleTimer.h"
 #include "InjectVSyncSource.h"
-#include "LayerInfo.h"
+#include "OneShotTimer.h"
 #include "SchedulerUtils.h"
 #include "SurfaceFlingerProperties.h"
+#include "Timer.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncPredictor.h"
+#include "VSyncReactor.h"
+
+#define RETURN_IF_INVALID_HANDLE(handle, ...)                        \
+    do {                                                             \
+        if (mConnections.count(handle) == 0) {                       \
+            ALOGE("Invalid connection handle %" PRIuPTR, handle.id); \
+            return __VA_ARGS__;                                      \
+        }                                                            \
+    } while (false)
 
 namespace android {
 
-using namespace android::hardware::configstore;
-using namespace android::hardware::configstore::V1_0;
-using namespace android::sysprop;
+std::unique_ptr<DispSync> createDispSync() {
+    // TODO (140302863) remove this and use the vsync_reactor system.
+    if (property_get_bool("debug.sf.vsync_reactor", true)) {
+        // TODO (144707443) tune Predictor tunables.
+        static constexpr int default_rate = 60;
+        static constexpr auto initial_period =
+                std::chrono::duration<nsecs_t, std::ratio<1, default_rate>>(1);
+        static constexpr size_t vsyncTimestampHistorySize = 20;
+        static constexpr size_t minimumSamplesForPrediction = 6;
+        static constexpr uint32_t discardOutlierPercent = 20;
+        auto tracker = std::make_unique<
+                scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>(
+                                                   initial_period)
+                                                   .count(),
+                                           vsyncTimestampHistorySize, minimumSamplesForPrediction,
+                                           discardOutlierPercent);
 
-#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
+        static constexpr auto vsyncMoveThreshold =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(3ms);
+        static constexpr auto timerSlack =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(500us);
+        auto dispatch = std::make_unique<
+                scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker,
+                                                    timerSlack.count(), vsyncMoveThreshold.count());
 
-std::atomic<int64_t> Scheduler::sNextId = 0;
+        static constexpr size_t pendingFenceLimit = 20;
+        return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(),
+                                                         std::move(dispatch), std::move(tracker),
+                                                         pendingFenceLimit);
+    } else {
+        return std::make_unique<impl::DispSync>("SchedulerDispSync",
+                                                sysprop::running_without_sync_framework(true));
+    }
+}
 
 Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
-                     const scheduler::RefreshRateConfigs& refreshRateConfig)
-      : mHasSyncFramework(running_without_sync_framework(true)),
-        mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
-        mPrimaryHWVsyncEnabled(false),
-        mHWVsyncAvailable(false),
-        mRefreshRateConfigs(refreshRateConfig) {
-    // 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);
+                     const scheduler::RefreshRateConfigs& refreshRateConfig,
+                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+      : mPrimaryDispSync(createDispSync()),
+        mEventControlThread(new impl::EventControlThread(std::move(function))),
+        mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+        mSchedulerCallback(schedulerCallback),
+        mRefreshRateConfigs(refreshRateConfig),
+        mUseContentDetectionV2(useContentDetectionV2) {
+    using namespace sysprop;
 
-    mSetIdleTimerMs = set_idle_timer_ms(0);
-    mSupportKernelTimer = support_kernel_idle_timer(false);
-
-    mSetTouchTimerMs = set_touch_timer_ms(0);
-    mSetDisplayPowerTimerMs = set_display_power_timer_ms(0);
-
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.sf.set_idle_timer_ms", value, "0");
-    int int_value = atoi(value);
-    if (int_value) {
-        mSetIdleTimerMs = atoi(value);
+    if (mUseContentDetectionV2) {
+        mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+    } else {
+        mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
     }
 
-    if (mSetIdleTimerMs > 0) {
-        if (mSupportKernelTimer) {
-            mIdleTimer =
-                    std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
-                                                                   mSetIdleTimerMs),
-                                                           [this] { resetKernelTimerCallback(); },
-                                                           [this] {
-                                                               expiredKernelTimerCallback();
-                                                           });
-        } else {
-            mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
-                                                                        mSetIdleTimerMs),
-                                                                [this] { resetTimerCallback(); },
-                                                                [this] { expiredTimerCallback(); });
-        }
+    const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0);
+
+    if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
+        const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback
+                                                  : &Scheduler::idleTimerCallback;
+        mIdleTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this, callback] { std::invoke(callback, this, TimerState::Reset); },
+                [this, callback] { std::invoke(callback, this, TimerState::Expired); });
         mIdleTimer->start();
     }
 
-    if (mSetTouchTimerMs > 0) {
+    if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
         // Touch events are coming to SF every 100ms, so the timer needs to be higher than that
-        mTouchTimer =
-                std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetTouchTimerMs),
-                                                       [this] { resetTouchTimerCallback(); },
-                                                       [this] { expiredTouchTimerCallback(); });
+        mTouchTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this] { touchTimerCallback(TimerState::Reset); },
+                [this] { touchTimerCallback(TimerState::Expired); });
         mTouchTimer->start();
     }
 
-    if (mSetDisplayPowerTimerMs > 0) {
-        mDisplayPowerTimer =
-                std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
-                                                               mSetDisplayPowerTimerMs),
-                                                       [this] { resetDisplayPowerTimerCallback(); },
-                                                       [this] {
-                                                           expiredDisplayPowerTimerCallback();
-                                                       });
+    if (const int64_t millis = set_display_power_timer_ms(0); millis > 0) {
+        mDisplayPowerTimer.emplace(
+                std::chrono::milliseconds(millis),
+                [this] { displayPowerTimerCallback(TimerState::Reset); },
+                [this] { displayPowerTimerCallback(TimerState::Expired); });
         mDisplayPowerTimer->start();
     }
 }
 
+Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
+                     std::unique_ptr<EventControlThread> eventControlThread,
+                     const scheduler::RefreshRateConfigs& configs,
+                     ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+      : mPrimaryDispSync(std::move(primaryDispSync)),
+        mEventControlThread(std::move(eventControlThread)),
+        mSupportKernelTimer(false),
+        mSchedulerCallback(schedulerCallback),
+        mRefreshRateConfigs(configs),
+        mUseContentDetectionV2(useContentDetectionV2) {}
+
 Scheduler::~Scheduler() {
-    // Ensure the IdleTimer thread is joined before we start destroying state.
+    // Ensure the OneShotTimer threads are joined before we start destroying state.
     mDisplayPowerTimer.reset();
     mTouchTimer.reset();
     mIdleTimer.reset();
 }
 
-sp<Scheduler::ConnectionHandle> Scheduler::createConnection(
-        const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
-        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,
-                            offsetThresholdForNextVsync, std::move(interceptCallback));
-
-    auto eventThreadConnection =
-            createConnectionInternal(eventThread.get(), std::move(resyncCallback),
-                                     ISurfaceComposer::eConfigChangedSuppress);
-    mConnections.emplace(id,
-                         std::make_unique<Connection>(new ConnectionHandle(id),
-                                                      eventThreadConnection,
-                                                      std::move(eventThread)));
-    return mConnections[id]->handle;
+DispSync& Scheduler::getPrimaryDispSync() {
+    return *mPrimaryDispSync;
 }
 
-std::unique_ptr<EventThread> Scheduler::makeEventThread(
-        const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
-        nsecs_t offsetThresholdForNextVsync,
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
+                                                                  nsecs_t phaseOffsetNs) {
+    return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
+                                            true /* traceVsync */, name);
+}
+
+Scheduler::ConnectionHandle Scheduler::createConnection(
+        const char* connectionName, nsecs_t phaseOffsetNs,
         impl::EventThread::InterceptVSyncsCallback interceptCallback) {
-    std::unique_ptr<VSyncSource> eventThreadSource =
-            std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, offsetThresholdForNextVsync,
-                                             true, connectionName);
-    return std::make_unique<impl::EventThread>(std::move(eventThreadSource),
-                                               std::move(interceptCallback), connectionName);
+    auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
+    auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
+                                                           std::move(interceptCallback));
+    return createConnection(std::move(eventThread));
+}
+
+Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
+    const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
+    ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
+
+    auto connection =
+            createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress);
+
+    mConnections.emplace(handle, Connection{connection, std::move(eventThread)});
+    return handle;
 }
 
 sp<EventThreadConnection> Scheduler::createConnectionInternal(
-        EventThread* eventThread, ResyncCallback&& resyncCallback,
-        ISurfaceComposer::ConfigChanged configChanged) {
-    return eventThread->createEventConnection(std::move(resyncCallback), configChanged);
+        EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) {
+    return eventThread->createEventConnection([&] { resync(); }, configChanged);
 }
 
 sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection(
-        const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback,
-        ISurfaceComposer::ConfigChanged configChanged) {
-    RETURN_VALUE_IF_INVALID(nullptr);
-    return createConnectionInternal(mConnections[handle->id]->thread.get(),
-                                    std::move(resyncCallback), configChanged);
+        ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) {
+    RETURN_IF_INVALID_HANDLE(handle, nullptr);
+    return createConnectionInternal(mConnections[handle].thread.get(), configChanged);
 }
 
-EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) {
-    RETURN_VALUE_IF_INVALID(nullptr);
-    return mConnections[handle->id]->thread.get();
+sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle, nullptr);
+    return mConnections[handle].connection;
 }
 
-sp<EventThreadConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) {
-    RETURN_VALUE_IF_INVALID(nullptr);
-    return mConnections[handle->id]->eventConnection;
+void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                  bool connected) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onHotplugReceived(displayId, connected);
 }
 
-void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle,
-                                PhysicalDisplayId displayId, bool connected) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onHotplugReceived(displayId, connected);
+void Scheduler::onScreenAcquired(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onScreenAcquired();
 }
 
-void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onScreenAcquired();
+void Scheduler::onScreenReleased(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onScreenReleased();
 }
 
-void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onScreenReleased();
+void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
+                                HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
 }
 
-void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
-                                int32_t configId) {
-    RETURN_IF_INVALID();
-    mConnections[handle->id]->thread->onConfigChanged(displayId, configId);
+size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) {
+    RETURN_IF_INVALID_HANDLE(handle, 0);
+    return mConnections[handle].thread->getEventThreadConnectionCount();
 }
 
-void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const {
-    RETURN_IF_INVALID();
-    mConnections.at(handle->id)->thread->dump(result);
+void Scheduler::dump(ConnectionHandle handle, std::string& result) const {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections.at(handle).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::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) {
+    RETURN_IF_INVALID_HANDLE(handle);
+    mConnections[handle].thread->setPhaseOffset(phaseOffset);
 }
 
 void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) {
@@ -224,6 +250,37 @@
     stats->vsyncPeriod = mPrimaryDispSync->getPeriod();
 }
 
+Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
+    if (mInjectVSyncs == enable) {
+        return {};
+    }
+
+    ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling");
+
+    if (!mInjectorConnectionHandle) {
+        auto vsyncSource = std::make_unique<InjectVSyncSource>();
+        mVSyncInjector = vsyncSource.get();
+
+        auto eventThread =
+                std::make_unique<impl::EventThread>(std::move(vsyncSource),
+                                                    impl::EventThread::InterceptVSyncsCallback());
+
+        mInjectorConnectionHandle = createConnection(std::move(eventThread));
+    }
+
+    mInjectVSyncs = enable;
+    return mInjectorConnectionHandle;
+}
+
+bool Scheduler::injectVSync(nsecs_t when) {
+    if (!mInjectVSyncs || !mVSyncInjector) {
+        return false;
+    }
+
+    mVSyncInjector->onInjectSyncEvent(when);
+    return true;
+}
+
 void Scheduler::enableHardwareVsync() {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
@@ -264,31 +321,18 @@
     setVsyncPeriod(period);
 }
 
-ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) {
-    std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState;
-    return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() {
-        if (const auto vsync = ptr.lock()) {
-            vsync->resync(getVsyncPeriod);
-        }
-    };
-}
-
-void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) {
+void Scheduler::resync() {
     static constexpr nsecs_t kIgnoreDelay = ms2ns(750);
 
     const nsecs_t now = systemTime();
-    const nsecs_t last = lastResyncTime.exchange(now);
+    const nsecs_t last = mLastResyncTime.exchange(now);
 
     if (now - last > kIgnoreDelay) {
-        scheduler.resyncToHardwareVsync(false, getVsyncPeriod());
+        resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().vsyncPeriod);
     }
 }
 
-void Scheduler::setRefreshSkipCount(int count) {
-    mPrimaryDispSync->setRefreshSkipCount(count);
-}
-
-void Scheduler::setVsyncPeriod(const nsecs_t period) {
+void Scheduler::setVsyncPeriod(nsecs_t period) {
     std::lock_guard<std::mutex> lock(mHWVsyncLock);
     mPrimaryDispSync->setPeriod(period);
 
@@ -299,13 +343,15 @@
     }
 }
 
-void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodFlushed) {
+void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                bool* periodFlushed) {
     bool needsHwVsync = false;
     *periodFlushed = false;
     { // Scope for the lock
         std::lock_guard<std::mutex> lock(mHWVsyncLock);
         if (mPrimaryHWVsyncEnabled) {
-            needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodFlushed);
+            needsHwVsync =
+                    mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
         }
     }
 
@@ -332,89 +378,77 @@
     return mPrimaryDispSync->expectedPresentTime();
 }
 
-void Scheduler::dumpPrimaryDispSync(std::string& result) const {
-    mPrimaryDispSync->dump(result);
+void Scheduler::registerLayer(Layer* layer) {
+    if (!mLayerHistory) return;
+
+    if (!mUseContentDetectionV2) {
+        const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
+        const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+                ? lowFps
+                : mRefreshRateConfigs.getMaxRefreshRate().fps;
+
+        mLayerHistory->registerLayer(layer, lowFps, highFps,
+                                     scheduler::LayerHistory::LayerVoteType::Heuristic);
+    } else {
+        if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+            mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+                                         mRefreshRateConfigs.getMaxRefreshRate().fps,
+                                         scheduler::LayerHistory::LayerVoteType::Min);
+        } else if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
+            mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+                                         mRefreshRateConfigs.getMaxRefreshRate().fps,
+                                         scheduler::LayerHistory::LayerVoteType::NoVote);
+        } else {
+            mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+                                         mRefreshRateConfigs.getMaxRefreshRate().fps,
+                                         scheduler::LayerHistory::LayerVoteType::Heuristic);
+        }
+
+        // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
+        // API to set desired rate
+        {
+            const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
+            if (vote != 0 &&
+                layer->getName() ==
+                        "SurfaceView - "
+                        "com.google.android.youtube/"
+                        "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
+                layer->setFrameRate(
+                        Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
+            }
+        }
+    }
 }
 
-std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer(
-        std::string const& name, int windowType) {
-    RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER)
-            ? RefreshRateType::DEFAULT
-            : RefreshRateType::PERFORMANCE;
-
-    const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType);
-    const uint32_t performanceFps = (refreshRate) ? refreshRate->fps : 0;
-
-    const auto defaultRefreshRate = mRefreshRateConfigs.getRefreshRate(RefreshRateType::DEFAULT);
-    const uint32_t defaultFps = (defaultRefreshRate) ? defaultRefreshRate->fps : 0;
-    return mLayerHistory.createLayer(name, defaultFps, performanceFps);
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
+    if (mLayerHistory) {
+        mLayerHistory->record(layer, presentTime, systemTime());
+    }
 }
 
-void Scheduler::addLayerPresentTimeAndHDR(
-        const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
-        nsecs_t presentTime, bool isHDR) {
-    mLayerHistory.insert(layerHandle, presentTime, isHDR);
-}
+void Scheduler::chooseRefreshRateForContent() {
+    if (!mLayerHistory) return;
 
-void Scheduler::setLayerVisibility(
-        const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible) {
-    mLayerHistory.setVisibility(layerHandle, visible);
-}
+    ATRACE_CALL();
 
-void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
-    fn(*mPrimaryDispSync);
-}
-
-void Scheduler::updateFpsBasedOnContent() {
-    auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR();
-    const uint32_t refreshRateRound = std::round(refreshRate);
-    RefreshRateType newRefreshRateType;
+    scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+    HwcConfigIndexType newConfigId;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) {
+        if (mFeatures.contentRequirements == summary) {
             return;
         }
-        mContentRefreshRate = refreshRateRound;
-        ATRACE_INT("ContentFPS", mContentRefreshRate);
+        mFeatures.contentRequirements = summary;
+        mFeatures.contentDetection =
+                !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
 
-        mIsHDRContent = isHDR;
-        ATRACE_INT("ContentHDR", mIsHDRContent);
-
-        mCurrentContentFeatureState = refreshRateRound > 0
-                ? ContentFeatureState::CONTENT_DETECTION_ON
-                : ContentFeatureState::CONTENT_DETECTION_OFF;
-        newRefreshRateType = calculateRefreshRateType();
-        if (mRefreshRateType == newRefreshRateType) {
+        newConfigId = calculateRefreshRateConfigIndexType();
+        if (mFeatures.configId == newConfigId) {
             return;
         }
-        mRefreshRateType = newRefreshRateType;
-    }
-    changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
-}
-
-void Scheduler::setChangeRefreshRateCallback(
-        const ChangeRefreshRateCallback&& changeRefreshRateCallback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mChangeRefreshRateCallback = changeRefreshRateCallback;
-}
-
-void Scheduler::setGetCurrentRefreshRateTypeCallback(
-        const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateTypeCallback) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mGetCurrentRefreshRateTypeCallback = getCurrentRefreshRateTypeCallback;
-}
-
-void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    mGetVsyncPeriod = getVsyncPeriod;
-}
-
-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;
+        mFeatures.configId = newConfigId;
+        auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+        mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
     }
 }
 
@@ -425,23 +459,28 @@
 }
 
 void Scheduler::notifyTouchEvent() {
-    if (mTouchTimer) {
-        mTouchTimer->reset();
-    }
-
-    if (mSupportKernelTimer) {
-        resetIdleTimer();
-    }
+    if (!mTouchTimer) return;
 
     // Touch event will boost the refresh rate to performance.
-    // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clearHistory();
+    // Clear Layer History to get fresh FPS detection.
+    // NOTE: Instead of checking all the layers, we should be checking the layer
+    // that is currently on top. b/142507166 will give us this capability.
+    std::lock_guard<std::mutex> lock(mFeatureStateLock);
+    if (mLayerHistory && !layerHistoryHasClientSpecifiedFrameRate()) {
+        mLayerHistory->clear();
+
+        mTouchTimer->reset();
+
+        if (mSupportKernelTimer && mIdleTimer) {
+            mIdleTimer->reset();
+        }
+    }
 }
 
 void Scheduler::setDisplayPowerState(bool normal) {
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
-        mIsDisplayPowerStateNormal = normal;
+        mFeatures.isDisplayPowerStateNormal = normal;
     }
 
     if (mDisplayPowerTimer) {
@@ -450,161 +489,193 @@
 
     // Display Power event will boost the refresh rate to performance.
     // Clear Layer History to get fresh FPS detection
-    mLayerHistory.clearHistory();
+    if (mLayerHistory) {
+        mLayerHistory->clear();
+    }
 }
 
-void Scheduler::resetTimerCallback() {
-    handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
-    ATRACE_INT("ExpiredIdleTimer", 0);
-}
+void Scheduler::kernelIdleTimerCallback(TimerState state) {
+    ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
 
-void Scheduler::resetKernelTimerCallback() {
-    ATRACE_INT("ExpiredKernelIdleTimer", 0);
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) {
+    // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
+    // magic number
+    const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+    constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
+    if (state == TimerState::Reset && refreshRate.fps > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
         // If we're not in performance mode then the kernel timer shouldn't do
         // anything, as the refresh rate during DPU power collapse will be the
         // same.
-        if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) {
-            resyncToHardwareVsync(true, mGetVsyncPeriod());
-        }
+        resyncToHardwareVsync(true /* makeAvailable */, refreshRate.vsyncPeriod);
+    } else if (state == TimerState::Expired && refreshRate.fps <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
+        // Disable HW VSYNC if the timer expired, as we don't need it enabled if
+        // we're not pushing frames, and if we're in PERFORMANCE mode then we'll
+        // need to update the DispSync model anyway.
+        disableHardwareVsync(false /* makeUnavailable */);
     }
 }
 
-void Scheduler::expiredTimerCallback() {
-    handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
-    ATRACE_INT("ExpiredIdleTimer", 1);
+void Scheduler::idleTimerCallback(TimerState state) {
+    handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */);
+    ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
 }
 
-void Scheduler::resetTouchTimerCallback() {
-    handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
-    ATRACE_INT("TouchState", 1);
+void Scheduler::touchTimerCallback(TimerState state) {
+    const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
+    handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */);
+    ATRACE_INT("TouchState", static_cast<int>(touch));
 }
 
-void Scheduler::expiredTouchTimerCallback() {
-    handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
-    ATRACE_INT("TouchState", 0);
+void Scheduler::displayPowerTimerCallback(TimerState state) {
+    handleTimerStateChanged(&mFeatures.displayPowerTimer, state,
+                            true /* eventOnContentDetection */);
+    ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
 }
 
-void Scheduler::resetDisplayPowerTimerCallback() {
-    handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
-    ATRACE_INT("ExpiredDisplayPowerTimer", 0);
-}
+void Scheduler::dump(std::string& result) const {
+    using base::StringAppendF;
+    const char* const states[] = {"off", "on"};
 
-void Scheduler::expiredDisplayPowerTimerCallback() {
-    handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
-    ATRACE_INT("ExpiredDisplayPowerTimer", 1);
-}
-
-void Scheduler::expiredKernelTimerCallback() {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    ATRACE_INT("ExpiredKernelIdleTimer", 1);
-    if (mGetCurrentRefreshRateTypeCallback) {
-        if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) {
-            // Disable HW Vsync if the timer expired, as we don't need it
-            // enabled if we're not pushing frames, and if we're in PERFORMANCE
-            // mode then we'll need to re-update the DispSync model anyways.
-            disableHardwareVsync(false);
-        }
-    }
-}
-
-std::string Scheduler::doDump() {
-    std::ostringstream stream;
-    stream << "+  Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
-    stream << "+  Touch timer interval: " << mSetTouchTimerMs << " ms" << std::endl;
-    return stream.str();
+    StringAppendF(&result, "+  Idle timer: %s\n",
+                  mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
+    StringAppendF(&result, "+  Touch timer: %s\n\n",
+                  mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
 }
 
 template <class T>
 void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
     ConfigEvent event = ConfigEvent::None;
-    RefreshRateType newRefreshRateType;
+    HwcConfigIndexType newConfigId;
     {
         std::lock_guard<std::mutex> lock(mFeatureStateLock);
         if (*currentState == newState) {
             return;
         }
         *currentState = newState;
-        newRefreshRateType = calculateRefreshRateType();
-        if (mRefreshRateType == newRefreshRateType) {
+        newConfigId = calculateRefreshRateConfigIndexType();
+        if (mFeatures.configId == newConfigId) {
             return;
         }
-        mRefreshRateType = newRefreshRateType;
-        if (eventOnContentDetection &&
-            mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
+        mFeatures.configId = newConfigId;
+        if (eventOnContentDetection && mFeatures.contentDetection == ContentDetectionState::On) {
             event = ConfigEvent::Changed;
         }
     }
-    changeRefreshRate(newRefreshRateType, event);
+    const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+    mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
 }
 
-Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
-    // HDR content is not supported on PERFORMANCE mode
-    if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) {
-        return RefreshRateType::DEFAULT;
-    }
-
-    // If Display Power is not in normal operation we want to be in performance mode.
-    // When coming back to normal mode, a grace period is given with DisplayPowerTimer
-    if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
-        return RefreshRateType::PERFORMANCE;
-    }
-
-    // As long as touch is active we want to be in performance mode
-    if (mCurrentTouchState == TouchState::ACTIVE) {
-        return RefreshRateType::PERFORMANCE;
-    }
-
-    // If timer has expired as it means there is no new content on the screen
-    if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) {
-        return RefreshRateType::DEFAULT;
-    }
-
-    // If content detection is off we choose performance as we don't know the content fps
-    if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) {
-        return RefreshRateType::PERFORMANCE;
-    }
-
-    // Content detection is on, find the appropriate refresh rate with minimal error
-    auto begin = mRefreshRateConfigs.getRefreshRates().cbegin();
-
-    // Skip POWER_SAVING config as it is not a real config
-    if (begin->first == RefreshRateType::POWER_SAVING) {
-        ++begin;
-    }
-    auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRates().cend(),
-                            [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool {
-                                return std::abs(l.second->fps - static_cast<float>(rate)) <
-                                        std::abs(r.second->fps - static_cast<float>(rate));
-                            });
-    RefreshRateType currRefreshRateType = iter->first;
-
-    // Some content aligns better on higher refresh rate. For example for 45fps we should choose
-    // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
-    // align well with both
-    constexpr float MARGIN = 0.05f;
-    float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps /
-            float(mContentRefreshRate);
-    if (std::abs(std::round(ratio) - ratio) > MARGIN) {
-        while (iter != mRefreshRateConfigs.getRefreshRates().cend()) {
-            ratio = iter->second->fps / float(mContentRefreshRate);
-
-            if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
-                currRefreshRateType = iter->first;
-                break;
-            }
-            ++iter;
+bool Scheduler::layerHistoryHasClientSpecifiedFrameRate() {
+    // Traverse all the layers to see if any of them requested frame rate.
+    for (const auto& layer : mFeatures.contentRequirements) {
+        if (layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitDefault ||
+            layer.vote == scheduler::RefreshRateConfigs::LayerVoteType::ExplicitExactOrMultiple) {
+            return true;
         }
     }
 
-    return currRefreshRateType;
+    return false;
 }
 
-void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
-    std::lock_guard<std::mutex> lock(mCallbackLock);
-    if (mChangeRefreshRateCallback) {
-        mChangeRefreshRateCallback(refreshRateType, configEvent);
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
+    // This block of the code checks whether any layers used the SetFrameRate API. If they have,
+    // their request should be honored depending on other active layers.
+    if (layerHistoryHasClientSpecifiedFrameRate()) {
+        if (!mUseContentDetectionV2) {
+            return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements)
+                    .configId;
+        } else {
+            return mRefreshRateConfigs.getRefreshRateForContentV2(mFeatures.contentRequirements)
+                    .configId;
+        }
+    }
+
+    // If the layer history doesn't have the frame rate specified, check for other features and
+    // honor them. NOTE: If we remove the kernel idle timer, and use our internal idle timer, this
+    // code will have to be refactored. If Display Power is not in normal operation we want to be in
+    // performance mode. When coming back to normal mode, a grace period is given with
+    // DisplayPowerTimer.
+    if (mDisplayPowerTimer &&
+        (!mFeatures.isDisplayPowerStateNormal ||
+         mFeatures.displayPowerTimer == TimerState::Reset)) {
+        return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+    }
+
+    // As long as touch is active we want to be in performance mode.
+    if (mTouchTimer && mFeatures.touch == TouchState::Active) {
+        return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+    }
+
+    // If timer has expired as it means there is no new content on the screen.
+    if (mIdleTimer && mFeatures.idleTimer == TimerState::Expired) {
+        return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
+    }
+
+    if (!mUseContentDetectionV2) {
+        // If content detection is off we choose performance as we don't know the content fps.
+        if (mFeatures.contentDetection == ContentDetectionState::Off) {
+            // NOTE: V1 always calls this, but this is not a default behavior for V2.
+            return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
+        }
+
+        // Content detection is on, find the appropriate refresh rate with minimal error
+        return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements).configId;
+    }
+
+    // Content detection is on, find the appropriate refresh rate with minimal error
+    if (mFeatures.contentDetection == ContentDetectionState::On) {
+        return mRefreshRateConfigs.getRefreshRateForContentV2(mFeatures.contentRequirements)
+                .configId;
+    }
+
+    // There are no signals for refresh rate, just leave it as is.
+    return mRefreshRateConfigs.getCurrentRefreshRateByPolicy().configId;
+}
+
+std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
+    std::lock_guard<std::mutex> lock(mFeatureStateLock);
+    // Make sure that the default config ID is first updated, before returned.
+    if (mFeatures.configId.has_value()) {
+        mFeatures.configId = calculateRefreshRateConfigIndexType();
+    }
+    return mFeatures.configId;
+}
+
+void Scheduler::onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline) {
+    if (timeline.refreshRequired) {
+        mSchedulerCallback.repaintEverythingForHWC();
+    }
+
+    std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+    mLastVsyncPeriodChangeTimeline = std::make_optional(timeline);
+
+    const auto maxAppliedTime = systemTime() + MAX_VSYNC_APPLIED_TIME.count();
+    if (timeline.newVsyncAppliedTimeNanos > maxAppliedTime) {
+        mLastVsyncPeriodChangeTimeline->newVsyncAppliedTimeNanos = maxAppliedTime;
+    }
+}
+
+void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
+    bool callRepaint = false;
+    {
+        std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+        if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
+            if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
+                mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+            } else {
+                // We need to send another refresh as refreshTimeNanos is still in the future
+                callRepaint = true;
+            }
+        }
+    }
+
+    if (callRepaint) {
+        mSchedulerCallback.repaintEverythingForHWC();
+    }
+}
+
+void Scheduler::onPrimaryDisplayAreaChanged(uint32_t displayArea) {
+    if (mLayerHistory) {
+        mLayerHistory->setDisplayArea(displayArea);
     }
 }
 
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 5d8bb4c..81051be 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -16,309 +16,238 @@
 
 #pragma once
 
-#include <cstdint>
+#include <atomic>
 #include <functional>
 #include <memory>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
 
-#include <ui/DisplayStatInfo.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #include <ui/GraphicTypes.h>
+#pragma clang diagnostic pop
 
-#include "DispSync.h"
 #include "EventControlThread.h"
 #include "EventThread.h"
-#include "IdleTimer.h"
-#include "InjectVSyncSource.h"
 #include "LayerHistory.h"
+#include "OneShotTimer.h"
 #include "RefreshRateConfigs.h"
 #include "SchedulerUtils.h"
 
 namespace android {
 
-class EventControlThread;
+using namespace std::chrono_literals;
+
+class DispSync;
+class FenceTime;
+class InjectVSyncSource;
+struct DisplayStateInfo;
+
+class ISchedulerCallback {
+public:
+    virtual ~ISchedulerCallback() = default;
+    virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+                                   scheduler::RefreshRateConfigEvent) = 0;
+    virtual void repaintEverythingForHWC() = 0;
+};
 
 class Scheduler {
 public:
-    // Enum to keep track of whether we trigger event to notify choreographer of config changes.
-    enum class ConfigEvent { None, Changed };
+    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
+    using ConfigEvent = scheduler::RefreshRateConfigEvent;
 
-    // logical or operator with the semantics of at least one of the events is Changed
-    friend ConfigEvent operator|(const ConfigEvent& first, const ConfigEvent& second) {
-        if (first == ConfigEvent::Changed) return ConfigEvent::Changed;
-        if (second == ConfigEvent::Changed) return ConfigEvent::Changed;
-        return ConfigEvent::None;
-    }
-
-    using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-    using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>;
-    using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
-    using GetVsyncPeriod = std::function<nsecs_t()>;
-
-    // Enum to indicate whether to start the transaction early, or at vsync time.
+    // Indicates 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;
-    };
-
-    // Stores per-display state about VSYNC.
-    struct VsyncState {
-        explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {}
-
-        void resync(const GetVsyncPeriod&);
-
-        Scheduler& scheduler;
-        std::atomic<nsecs_t> lastResyncTime = 0;
-    };
-
-    explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
-                       const scheduler::RefreshRateConfigs& refreshRateConfig);
+    Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
+              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+              bool useContentDetectionV2);
 
     virtual ~Scheduler();
 
-    /** Creates an EventThread connection. */
-    sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
-                                          nsecs_t offsetThresholdForNextVsync, ResyncCallback,
-                                          impl::EventThread::InterceptVSyncsCallback);
+    DispSync& getPrimaryDispSync();
 
-    sp<IDisplayEventConnection> createDisplayEventConnection(
-            const sp<ConnectionHandle>& handle, ResyncCallback,
-            ISurfaceComposer::ConfigChanged configChanged);
+    using ConnectionHandle = scheduler::ConnectionHandle;
+    ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
+                                      impl::EventThread::InterceptVSyncsCallback);
 
-    // Getter methods.
-    EventThread* getEventThread(const sp<ConnectionHandle>& handle);
+    sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
+                                                             ISurfaceComposer::ConfigChanged);
 
-    // Provides access to the DispSync object for the primary display.
-    void withPrimaryDispSync(std::function<void(DispSync&)> const& fn);
+    sp<EventThreadConnection> getEventConnection(ConnectionHandle);
 
-    sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
+    void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
+    void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId,
+                         nsecs_t vsyncPeriod);
 
-    // Should be called when receiving a hotplug event.
-    void hotplugReceived(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
-                         bool connected);
+    void onScreenAcquired(ConnectionHandle);
+    void onScreenReleased(ConnectionHandle);
 
-    // 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 display config changed
-    void onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId,
-                         int32_t configId);
-
-    // 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);
+    // Modifies phase offset in the event thread.
+    void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset);
 
     void getDisplayStatInfo(DisplayStatInfo* stats);
 
+    // Returns injector handle if injection has toggled, or an invalid handle otherwise.
+    ConnectionHandle enableVSyncInjection(bool enable);
+
+    // Returns false if injection is disabled.
+    bool injectVSync(nsecs_t when);
+
     void enableHardwareVsync();
     void disableHardwareVsync(bool makeUnavailable);
+
     // Resyncs the scheduler to hardware vsync.
     // If makeAvailable is true, then hardware vsync will be turned on.
     // Otherwise, if hardware vsync is not already enabled then this method will
     // no-op.
     // The period is the vsync period from the current display configuration.
     void resyncToHardwareVsync(bool makeAvailable, nsecs_t period);
-    // Creates a callback for resyncing.
-    ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod);
-    void setRefreshSkipCount(int count);
+    void resync();
+
     // Passes a vsync sample to DispSync. periodFlushed will be true if
     // DispSync detected that the vsync period changed, and false otherwise.
-    void addResyncSample(const nsecs_t timestamp, bool* periodFlushed);
-    void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
+    void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed);
+    void addPresentFence(const std::shared_ptr<FenceTime>&);
     void setIgnorePresentFences(bool ignore);
     nsecs_t getDispSyncExpectedPresentTime();
-    // Registers the layer in the scheduler, and returns the handle for future references.
-    std::unique_ptr<scheduler::LayerHistory::LayerHandle> registerLayer(std::string const& name,
-                                                                        int windowType);
 
-    // Stores present time for a layer.
-    void addLayerPresentTimeAndHDR(
-            const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle,
-            nsecs_t presentTime, bool isHDR);
-    // Stores visibility for a layer.
-    void setLayerVisibility(
-            const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible);
-    // Updates FPS based on the most content presented.
-    void updateFpsBasedOnContent();
-    // Callback that gets invoked when Scheduler wants to change the refresh rate.
-    void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback);
-    void setGetCurrentRefreshRateTypeCallback(
-            const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateType);
-    void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
+    // Layers are registered on creation, and unregistered when the weak reference expires.
+    void registerLayer(Layer*);
+    void recordLayerHistory(Layer*, nsecs_t presentTime);
 
-    // Returns whether idle timer is enabled or not
-    bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
+    // Detects content using layer history, and selects a matching refresh rate.
+    void chooseRefreshRateForContent();
 
-    // Function that resets the idle timer.
+    bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
     void resetIdleTimer();
 
     // Function that resets the touch timer.
     void notifyTouchEvent();
 
-    // Function that sets whether display power mode is normal or not.
     void setDisplayPowerState(bool normal);
 
-    // Returns relevant information about Scheduler for dumpsys purposes.
-    std::string doDump();
+    void dump(std::string&) const;
+    void dump(ConnectionHandle, std::string&) const;
 
-    // calls DispSync::dump() on primary disp sync
-    void dumpPrimaryDispSync(std::string& result) const;
+    // Get the appropriate refresh for current conditions.
+    std::optional<HwcConfigIndexType> getPreferredConfigId();
 
-protected:
-    virtual std::unique_ptr<EventThread> makeEventThread(
-            const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs,
-            nsecs_t offsetThresholdForNextVsync,
-            impl::EventThread::InterceptVSyncsCallback interceptCallback);
+    // Notifies the scheduler about a refresh rate timeline change.
+    void onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline);
+
+    // Notifies the scheduler when the display was refreshed
+    void onDisplayRefreshed(nsecs_t timestamp);
+
+    // Notifies the scheduler when the display size has changed. Called from SF's main thread
+    void onPrimaryDisplayAreaChanged(uint32_t displayArea);
+
+    size_t getEventThreadConnectionCount(ConnectionHandle handle);
 
 private:
     friend class TestableScheduler;
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
-    enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
-    enum class IdleTimerState { EXPIRED, RESET };
-    enum class TouchState { INACTIVE, ACTIVE };
-    enum class DisplayPowerTimerState { EXPIRED, RESET };
+    enum class ContentDetectionState { Off, On };
+    enum class TimerState { Reset, Expired };
+    enum class TouchState { Inactive, Active };
 
-    // Creates a connection on the given EventThread and forwards the given callbacks.
-    sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
+    // Used by tests to inject mocks.
+    Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
+              const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
+              bool useContentDetectionV2);
+
+    std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
+
+    // Create a connection on the given EventThread.
+    ConnectionHandle createConnection(std::unique_ptr<EventThread>);
+    sp<EventThreadConnection> createConnectionInternal(EventThread*,
                                                        ISurfaceComposer::ConfigChanged);
 
-    nsecs_t calculateAverage() const;
-    void updateFrameSkipping(const int64_t skipCount);
+    // Update feature state machine to given state when corresponding timer resets or expires.
+    void kernelIdleTimerCallback(TimerState);
+    void idleTimerCallback(TimerState);
+    void touchTimerCallback(TimerState);
+    void displayPowerTimerCallback(TimerState);
 
-    // Function that is called when the timer resets.
-    void resetTimerCallback();
-    // Function that is called when the timer expires.
-    void expiredTimerCallback();
-    // Function that is called when the timer resets when paired with a display
-    // driver timeout in the kernel. This enables hardware vsync when we move
-    // out from idle.
-    void resetKernelTimerCallback();
-    // Function that is called when the timer expires when paired with a display
-    // driver timeout in the kernel. This disables hardware vsync when we move
-    // into idle.
-    void expiredKernelTimerCallback();
-    // Function that is called when the touch timer resets.
-    void resetTouchTimerCallback();
-    // Function that is called when the touch timer expires.
-    void expiredTouchTimerCallback();
-    // Function that is called when the display power timer resets.
-    void resetDisplayPowerTimerCallback();
-    // Function that is called when the display power timer expires.
-    void expiredDisplayPowerTimerCallback();
-    // Sets vsync period.
-    void setVsyncPeriod(const nsecs_t period);
     // handles various timer features to change the refresh rate.
     template <class T>
     void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
-    // Calculate the new refresh rate type
-    RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
-    // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
-    void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
 
-    // Helper function to calculate error frames
-    float getErrorFrames(float contentFps, float configFps);
+    void setVsyncPeriod(nsecs_t period);
 
-    // If fences from sync Framework are supported.
-    const bool mHasSyncFramework;
+    // This function checks whether individual features that are affecting the refresh rate
+    // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
+    // for the suggested refresh rate.
+    HwcConfigIndexType calculateRefreshRateConfigIndexType() REQUIRES(mFeatureStateLock);
 
-    // The offset in nanoseconds to use, when DispSync timestamps present fence
-    // signaling time.
-    nsecs_t mDispSyncPresentTimeOffset;
+    bool layerHistoryHasClientSpecifiedFrameRate() REQUIRES(mFeatureStateLock);
 
-    // Each connection has it's own ID. This variable keeps track of the count.
-    static std::atomic<int64_t> sNextId;
+    // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
+    struct Connection {
+        sp<EventThreadConnection> connection;
+        std::unique_ptr<EventThread> thread;
+    };
 
-    // Connections are stored in a map <connection ID, connection> for easy retrieval.
-    std::unordered_map<int64_t, std::unique_ptr<Connection>> mConnections;
+    ConnectionHandle::Id mNextConnectionHandleId = 0;
+    std::unordered_map<ConnectionHandle, Connection> mConnections;
+
+    bool mInjectVSyncs = false;
+    InjectVSyncSource* mVSyncInjector = nullptr;
+    ConnectionHandle mInjectorConnectionHandle;
 
     std::mutex mHWVsyncLock;
-    bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock);
-    bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock);
-    const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)};
+    bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false;
+    bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false;
+
+    std::atomic<nsecs_t> mLastResyncTime = 0;
 
     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;
+    // Used to choose refresh rate if content detection is enabled.
+    std::unique_ptr<scheduler::LayerHistory> mLayerHistory;
 
-    // Historical information about individual layers. Used for predicting the refresh rate.
-    scheduler::LayerHistory mLayerHistory;
+    // Whether to use idle timer callbacks that support the kernel timer.
+    const bool mSupportKernelTimer;
 
-    // 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;
-    // Enables whether to use idle timer callbacks that support the kernel
-    // timer.
-    bool mSupportKernelTimer;
-
+    // Timer that records time between requests for next vsync.
+    std::optional<scheduler::OneShotTimer> mIdleTimer;
     // Timer used to monitor touch events.
-    int64_t mSetTouchTimerMs = 0;
-    std::unique_ptr<scheduler::IdleTimer> mTouchTimer;
-
+    std::optional<scheduler::OneShotTimer> mTouchTimer;
     // Timer used to monitor display power mode.
-    int64_t mSetDisplayPowerTimerMs = 0;
-    std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer;
+    std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
 
-    std::mutex mCallbackLock;
-    GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock);
-    ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
-    GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
+    ISchedulerCallback& mSchedulerCallback;
 
     // In order to make sure that the features don't override themselves, we need a state machine
     // to keep track which feature requested the config change.
     std::mutex mFeatureStateLock;
-    ContentFeatureState mCurrentContentFeatureState GUARDED_BY(mFeatureStateLock) =
-            ContentFeatureState::CONTENT_DETECTION_OFF;
-    IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
-    TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
-    DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) =
-            DisplayPowerTimerState::EXPIRED;
-    uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
-    RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
-    bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
-    bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true;
+
+    struct {
+        ContentDetectionState contentDetection = ContentDetectionState::Off;
+        TimerState idleTimer = TimerState::Reset;
+        TouchState touch = TouchState::Inactive;
+        TimerState displayPowerTimer = TimerState::Expired;
+
+        std::optional<HwcConfigIndexType> configId;
+        scheduler::LayerHistory::Summary contentRequirements;
+
+        bool isDisplayPowerStateNormal = true;
+    } mFeatures GUARDED_BY(mFeatureStateLock);
 
     const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
 
-    // Global config to force HDR content to work on DEFAULT refreshRate
-    static constexpr bool mForceHDRContentToDefaultRefreshRate = false;
+    std::mutex mVsyncTimelineLock;
+    std::optional<HWC2::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
+            GUARDED_BY(mVsyncTimelineLock);
+    static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
+
+    const bool mUseContentDetectionV2;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
index fb5414f..e8e0444 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -30,9 +30,10 @@
     }
 
     size_t n = v->size() / 2;
-    nth_element(v->begin(), v->begin() + n, v->end());
+    nth_element(v->begin(), v->begin() + static_cast<long>(n), v->end());
     return v->at(n);
 }
 
 } // namespace scheduler
 } // namespace android
+
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index ac10f83..04a4cd1 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -16,38 +16,27 @@
 
 #pragma once
 
-#include <chrono>
+#include <utils/Timers.h>
 #include <cinttypes>
 #include <numeric>
 #include <unordered_map>
 #include <vector>
 
-namespace android {
-namespace scheduler {
-using namespace std::chrono_literals;
+namespace android::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;
+// Opaque handle to scheduler connection.
+struct ConnectionHandle {
+    using Id = std::uintptr_t;
+    static constexpr Id INVALID_ID = static_cast<Id>(-1);
 
-// 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;
-static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff;
+    Id id = INVALID_ID;
 
-// This number is used when we try to determine how long do we keep layer information around
-// before we remove it. It is also used to determine how long the layer stays relevant.
-// This time period captures infrequent updates when playing YouTube video with static image,
-// or waiting idle in messaging app, when cursor is blinking.
-static constexpr std::chrono::nanoseconds OBSOLETE_TIME_EPSILON_NS = 1200ms;
+    explicit operator bool() const { return id != INVALID_ID; }
+};
 
-// Layer is considered low activity if the LOW_ACTIVITY_BUFFERS buffers come more than
-// LOW_ACTIVITY_EPSILON_NS  apart.
-// This is helping SF to vote for lower refresh rates when there is not activity
-// in screen.
-static constexpr int LOW_ACTIVITY_BUFFERS = 2;
-static constexpr std::chrono::nanoseconds LOW_ACTIVITY_EPSILON_NS = 250ms;
+inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) {
+    return lhs.id == rhs.id;
+}
 
 // Calculates the statistical mean (average) in the data structure (array, vector). The
 // function does not modify the contents of the array.
@@ -82,5 +71,27 @@
     return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first);
 }
 
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
+template <class T, size_t N>
+constexpr size_t arrayLen(T (&)[N]) {
+    return N;
+}
+
+static constexpr size_t max64print = std::numeric_limits<nsecs_t>::digits10 + 1;
+
+template <typename T>
+static inline T round(float f) {
+    return static_cast<T>(std::round(f));
+}
+
+} // namespace android::scheduler
+
+namespace std {
+
+template <>
+struct hash<android::scheduler::ConnectionHandle> {
+    size_t operator()(android::scheduler::ConnectionHandle handle) const {
+        return hash<android::scheduler::ConnectionHandle::Id>()(handle.id);
+    }
+};
+
+} // namespace std
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
new file mode 100644
index 0000000..e8ca0ba
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/StrongTyping.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
+namespace android {
+
+template <typename T, template <typename> class AbilityType>
+struct Ability {
+    T& base() { return static_cast<T&>(*this); }
+    T const& base() const { return static_cast<T const&>(*this); }
+};
+
+template <typename T>
+struct Add : Ability<T, Add> {
+    inline T operator+(T const& other) const { return T(this->base().value() + other.value()); }
+    inline T& operator++() {
+        ++this->base().value();
+        return this->base();
+    };
+    inline T operator++(int) {
+        T tmp(this->base());
+        operator++();
+        return tmp;
+    };
+    inline T& operator+=(T const& other) {
+        this->base().value() += other.value();
+        return this->base();
+    };
+};
+
+template <typename T>
+struct Compare : Ability<T, Compare> {
+    inline bool operator==(T const& other) const { return this->base().value() == other.value(); };
+    inline bool operator<(T const& other) const { return this->base().value() < other.value(); }
+    inline bool operator<=(T const& other) const { return (*this < other) || (*this == other); }
+    inline bool operator!=(T const& other) const { return !(*this == other); }
+    inline bool operator>=(T const& other) const { return !(*this < other); }
+    inline bool operator>(T const& other) const { return !(*this < other || *this == other); }
+};
+
+template <typename T>
+struct Hash : Ability<T, Hash> {
+    [[nodiscard]] std::size_t hash() const {
+        return std::hash<typename std::remove_const<
+                typename std::remove_reference<decltype(this->base().value())>::type>::type>{}(
+                this->base().value());
+    }
+};
+
+template <typename T, typename W, template <typename> class... Ability>
+struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
+    StrongTyping() : mValue(0) {}
+    explicit StrongTyping(T const& value) : mValue(value) {}
+    StrongTyping(StrongTyping const&) = default;
+    StrongTyping& operator=(StrongTyping const&) = default;
+    explicit inline operator T() const { return mValue; }
+    T const& value() const { return mValue; }
+    T& value() { return mValue; }
+
+private:
+    T mValue;
+};
+} // namespace android
+
+namespace std {
+template <typename T, typename W, template <typename> class... Ability>
+struct hash<android::StrongTyping<T, W, Ability...>> {
+    std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const {
+        return k.hash();
+    }
+};
+} // namespace std
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
new file mode 100644
index 0000000..38f0708
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.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 <utils/Timers.h>
+#include <functional>
+
+namespace android::scheduler {
+
+class Clock {
+public:
+    virtual ~Clock();
+    /*
+     * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
+     */
+    virtual nsecs_t now() const = 0;
+
+protected:
+    Clock() = default;
+    Clock(Clock const&) = delete;
+    Clock& operator=(Clock const&) = delete;
+};
+
+/*
+ * TimeKeeper is the interface for a single-shot timer primitive.
+ */
+class TimeKeeper : public Clock {
+public:
+    virtual ~TimeKeeper();
+
+    /*
+     * Arms callback to fired in time nanoseconds.
+     * There is only one timer, and subsequent calls will reset the callback function and the time.
+     */
+    virtual void alarmIn(std::function<void()> const& callback, nsecs_t time) = 0;
+
+    /*
+     * Cancels an existing pending callback
+     */
+    virtual void alarmCancel() = 0;
+
+protected:
+    TimeKeeper(TimeKeeper const&) = delete;
+    TimeKeeper& operator=(TimeKeeper const&) = delete;
+    TimeKeeper() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
new file mode 100644
index 0000000..8f81c2c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -0,0 +1,172 @@
+/*
+ * 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 "SchedulerTimer"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <log/log.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <sys/unistd.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <cstdint>
+
+#include "SchedulerUtils.h"
+#include "Timer.h"
+
+namespace android::scheduler {
+
+static constexpr size_t kReadPipe = 0;
+static constexpr size_t kWritePipe = 1;
+
+Timer::Timer()
+      : mTimerFd(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)),
+        mEpollFd(epoll_create1(EPOLL_CLOEXEC)) {
+    if (pipe2(mPipes.data(), O_CLOEXEC | O_NONBLOCK)) {
+        ALOGE("could not create TimerDispatch mPipes");
+    };
+
+    mDispatchThread = std::thread(std::bind(&Timer::dispatch, this));
+}
+
+Timer::~Timer() {
+    endDispatch();
+    mDispatchThread.join();
+
+    close(mPipes[kWritePipe]);
+    close(mPipes[kReadPipe]);
+    close(mEpollFd);
+    close(mTimerFd);
+}
+
+void Timer::endDispatch() {
+    static constexpr unsigned char end = 'e';
+    write(mPipes[kWritePipe], &end, sizeof(end));
+}
+
+nsecs_t Timer::now() const {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    using namespace std::literals;
+    static constexpr int ns_per_s =
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+
+    mCallback = cb;
+
+    struct itimerspec old_timer;
+    struct itimerspec new_timer {
+        .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+        .it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
+                     .tv_nsec = static_cast<long>(fireIn % ns_per_s)},
+    };
+
+    if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+        ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
+    }
+}
+
+void Timer::alarmCancel() {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+    struct itimerspec old_timer;
+    struct itimerspec new_timer {
+        .it_interval = {.tv_sec = 0, .tv_nsec = 0},
+        .it_value = {
+                .tv_sec = 0,
+                .tv_nsec = 0,
+        },
+    };
+
+    if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
+        ALOGW("Failed to disarm timerfd");
+    }
+}
+
+void Timer::dispatch() {
+    struct sched_param param = {0};
+    param.sched_priority = 2;
+    if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) != 0) {
+        ALOGW("Failed to set SCHED_FIFO on dispatch thread");
+    }
+
+    if (pthread_setname_np(pthread_self(), "TimerDispatch")) {
+        ALOGW("Failed to set thread name on dispatch thread");
+    }
+
+    enum DispatchType : uint32_t { TIMER, TERMINATE, MAX_DISPATCH_TYPE };
+    epoll_event timerEvent;
+    timerEvent.events = EPOLLIN;
+    timerEvent.data.u32 = DispatchType::TIMER;
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mTimerFd, &timerEvent) == -1) {
+        ALOGE("Error adding timer fd to epoll dispatch loop");
+        return;
+    }
+
+    epoll_event terminateEvent;
+    terminateEvent.events = EPOLLIN;
+    terminateEvent.data.u32 = DispatchType::TERMINATE;
+    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mPipes[kReadPipe], &terminateEvent) == -1) {
+        ALOGE("Error adding control fd to dispatch loop");
+        return;
+    }
+
+    uint64_t iteration = 0;
+    char const traceNamePrefix[] = "TimerIteration #";
+    static constexpr size_t maxlen = arrayLen(traceNamePrefix) + max64print;
+    std::array<char, maxlen> str_buffer;
+    auto timing = true;
+    while (timing) {
+        epoll_event events[DispatchType::MAX_DISPATCH_TYPE];
+        int nfds = epoll_wait(mEpollFd, events, DispatchType::MAX_DISPATCH_TYPE, -1);
+
+        if (ATRACE_ENABLED()) {
+            snprintf(str_buffer.data(), str_buffer.size(), "%s%" PRIu64, traceNamePrefix,
+                     iteration++);
+            ATRACE_NAME(str_buffer.data());
+        }
+
+        if (nfds == -1) {
+            if (errno != EINTR) {
+                timing = false;
+                continue;
+            }
+        }
+
+        for (auto i = 0; i < nfds; i++) {
+            if (events[i].data.u32 == DispatchType::TIMER) {
+                static uint64_t mIgnored = 0;
+                read(mTimerFd, &mIgnored, sizeof(mIgnored));
+                std::function<void()> cb;
+                {
+                    std::lock_guard<decltype(mMutex)> lk(mMutex);
+                    cb = mCallback;
+                }
+                if (cb) {
+                    cb();
+                }
+            }
+            if (events[i].data.u32 == DispatchType::TERMINATE) {
+                timing = false;
+            }
+        }
+    }
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
new file mode 100644
index 0000000..0ae82c8
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -0,0 +1,51 @@
+/*
+ * 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 "TimeKeeper.h"
+
+#include <android-base/thread_annotations.h>
+#include <array>
+#include <thread>
+
+namespace android::scheduler {
+
+class Timer : public TimeKeeper {
+public:
+    Timer();
+    ~Timer();
+    nsecs_t now() const final;
+
+    // NB: alarmIn and alarmCancel are threadsafe; with the last-returning function being effectual
+    //     Most users will want to serialize thes calls so as to be aware of the timer state.
+    void alarmIn(std::function<void()> const& cb, nsecs_t fireIn) final;
+    void alarmCancel() final;
+
+private:
+    int const mTimerFd;
+    int const mEpollFd;
+    std::array<int, 2> mPipes;
+
+    std::thread mDispatchThread;
+    void dispatch();
+    void endDispatch();
+
+    std::mutex mMutex;
+    std::function<void()> mCallback GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
new file mode 100644
index 0000000..a6fb3a4
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -0,0 +1,144 @@
+/*
+ * 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 <utils/Timers.h>
+#include <functional>
+#include <string>
+
+#include "StrongTyping.h"
+
+namespace android::scheduler {
+class TimeKeeper;
+class VSyncTracker;
+
+enum class ScheduleResult { Scheduled, CannotSchedule, Error };
+enum class CancelResult { Cancelled, TooLate, Error };
+
+/*
+ * VSyncDispatch is a class that will dispatch callbacks relative to system vsync events.
+ */
+class VSyncDispatch {
+public:
+    using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>;
+
+    virtual ~VSyncDispatch();
+
+    /*
+     * A callback that can be registered to be awoken at a given time relative to a vsync event.
+     * \param [in] vsyncTime The timestamp of the vsync the callback is for.
+     * \param [in] targetWakeupTime The timestamp of intended wakeup time of the cb.
+     *
+     */
+    using Callback = std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime)>;
+
+    /*
+     * Registers a callback that will be called at designated points on the vsync timeline.
+     * The callback can be scheduled, rescheduled targeting vsync times, or cancelled.
+     * The token returned must be cleaned up via unregisterCallback.
+     *
+     * \param [in] callbackFn   A function to schedule for callback. The resources needed to invoke
+     *                          callbackFn must have lifetimes encompassing the lifetime of the
+     *                          CallbackToken returned.
+     * \param [in] callbackName A human-readable, unique name to identify the callback.
+     * \return                  A token that can be used to schedule, reschedule, or cancel the
+     *                          invocation of callbackFn.
+     *
+     */
+    virtual CallbackToken registerCallback(Callback const& callbackFn,
+                                           std::string callbackName) = 0;
+
+    /*
+     * Unregisters a callback.
+     *
+     * \param [in] token        The callback to unregister.
+     *
+     */
+    virtual void unregisterCallback(CallbackToken token) = 0;
+
+    /*
+     * Schedules the registered callback to be dispatched.
+     *
+     * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event.
+     *
+     * The caller designates the earliest vsync event that should be targeted by the earliestVsync
+     * parameter.
+     * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync
+     * is the first vsync event time where ( predictedVsync >= earliestVsync ).
+     *
+     * If (workDuration - earliestVsync) is in the past, or if a callback has already been
+     * dispatched for the predictedVsync, an error will be returned.
+     *
+     * It is valid to reschedule a callback to a different time.
+     *
+     * \param [in] token           The callback to schedule.
+     * \param [in] workDuration    The time before the actual vsync time to invoke the callback
+     *                             associated with token.
+     * \param [in] earliestVsync   The targeted display time. This will be snapped to the closest
+     *                             predicted vsync time after earliestVsync.
+     * \return                     A ScheduleResult::Scheduled if callback was scheduled.
+     *                             A ScheduleResult::CannotSchedule
+     *                             if (workDuration - earliestVsync) is in the past, or
+     *                             if a callback was dispatched for the predictedVsync already.
+     *                             A ScheduleResult::Error if there was another error.
+     */
+    virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+                                    nsecs_t earliestVsync) = 0;
+
+    /* Cancels a scheduled callback, if possible.
+     *
+     * \param [in] token    The callback to cancel.
+     * \return              A CancelResult::TooLate if the callback was already dispatched.
+     *                      A CancelResult::Cancelled if the callback was successfully cancelled.
+     *                      A CancelResult::Error if there was an pre-condition violation.
+     */
+    virtual CancelResult cancel(CallbackToken token) = 0;
+
+protected:
+    VSyncDispatch() = default;
+    VSyncDispatch(VSyncDispatch const&) = delete;
+    VSyncDispatch& operator=(VSyncDispatch const&) = delete;
+};
+
+/*
+ * Helper class to operate on registered callbacks. It is up to user of the class to ensure
+ * that VsyncDispatch lifetime exceeds the lifetime of VSyncCallbackRegistation.
+ */
+class VSyncCallbackRegistration {
+public:
+    VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback const& callbackFn,
+                              std::string const& callbackName);
+    VSyncCallbackRegistration(VSyncCallbackRegistration&&);
+    VSyncCallbackRegistration& operator=(VSyncCallbackRegistration&&);
+    ~VSyncCallbackRegistration();
+
+    // See documentation for VSyncDispatch::schedule.
+    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync);
+
+    // See documentation for VSyncDispatch::cancel.
+    CancelResult cancel();
+
+private:
+    VSyncCallbackRegistration(VSyncCallbackRegistration const&) = delete;
+    VSyncCallbackRegistration& operator=(VSyncCallbackRegistration const&) = delete;
+
+    std::reference_wrapper<VSyncDispatch> mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    bool mValidToken;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
new file mode 100644
index 0000000..d0f18ab
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -0,0 +1,339 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <utils/Trace.h>
+#include <vector>
+
+#include "TimeKeeper.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+VSyncDispatch::~VSyncDispatch() = default;
+VSyncTracker::~VSyncTracker() = default;
+TimeKeeper::~TimeKeeper() = default;
+
+VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
+                                                           VSyncDispatch::Callback const& cb,
+                                                           nsecs_t minVsyncDistance)
+      : mName(name),
+        mCallback(cb),
+        mWorkDuration(0),
+        mEarliestVsync(0),
+        mMinVsyncDistance(minVsyncDistance) {}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
+    return mLastDispatchTime;
+}
+
+std::string_view VSyncDispatchTimerQueueEntry::name() const {
+    return mName;
+}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const {
+    if (!mArmedInfo) {
+        return {};
+    }
+    return {mArmedInfo->mActualWakeupTime};
+}
+
+std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const {
+    if (!mArmedInfo) {
+        return {};
+    }
+    return {mArmedInfo->mActualVsyncTime};
+}
+
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+                                                      VSyncTracker& tracker, nsecs_t now) {
+    auto nextVsyncTime =
+            tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+
+    bool const wouldSkipAVsyncTarget =
+            mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
+    if (wouldSkipAVsyncTarget) {
+        return ScheduleResult::Scheduled;
+    }
+
+    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
+            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
+             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
+    if (alreadyDispatchedForVsync) {
+        nextVsyncTime =
+                tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
+    }
+
+    auto const nextWakeupTime = nextVsyncTime - workDuration;
+    mWorkDuration = workDuration;
+    mEarliestVsync = earliestVsync;
+    mArmedInfo = {nextWakeupTime, nextVsyncTime};
+    return ScheduleResult::Scheduled;
+}
+
+void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
+    if (!mArmedInfo) {
+        return;
+    }
+    auto const nextVsyncTime =
+            tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
+    mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
+}
+
+void VSyncDispatchTimerQueueEntry::disarm() {
+    mArmedInfo.reset();
+}
+
+nsecs_t VSyncDispatchTimerQueueEntry::executing() {
+    mLastDispatchTime = mArmedInfo->mActualVsyncTime;
+    disarm();
+    return *mLastDispatchTime;
+}
+
+void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp) {
+    {
+        std::lock_guard<std::mutex> lk(mRunningMutex);
+        mRunning = true;
+    }
+
+    mCallback(vsyncTimestamp, wakeupTimestamp);
+
+    std::lock_guard<std::mutex> lk(mRunningMutex);
+    mRunning = false;
+    mCv.notify_all();
+}
+
+void VSyncDispatchTimerQueueEntry::ensureNotRunning() {
+    std::unique_lock<std::mutex> lk(mRunningMutex);
+    mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
+}
+
+VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
+                                                 VSyncTracker& tracker, nsecs_t timerSlack,
+                                                 nsecs_t minVsyncDistance)
+      : mTimeKeeper(std::move(tk)),
+        mTracker(tracker),
+        mTimerSlack(timerSlack),
+        mMinVsyncDistance(minVsyncDistance) {}
+
+VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    cancelTimer();
+}
+
+void VSyncDispatchTimerQueue::cancelTimer() {
+    mIntendedWakeupTime = kInvalidTime;
+    mTimeKeeper->alarmCancel();
+}
+
+void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
+    mIntendedWakeupTime = targetTime;
+    mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
+                         targetTime - now);
+}
+
+void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
+    rearmTimerSkippingUpdateFor(now, mCallbacks.end());
+}
+
+void VSyncDispatchTimerQueue::TraceBuffer::note(std::string_view name, nsecs_t alarmIn,
+                                                nsecs_t vsFor) {
+    if (ATRACE_ENABLED()) {
+        snprintf(str_buffer.data(), str_buffer.size(), "%.4s%s%" PRId64 "%s%" PRId64,
+                 name.substr(0, kMaxNamePrint).data(), kTraceNamePrefix, alarmIn,
+                 kTraceNameSeparator, vsFor);
+    }
+    ATRACE_NAME(str_buffer.data());
+}
+
+void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
+        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
+    std::optional<nsecs_t> min;
+    std::optional<nsecs_t> targetVsync;
+    std::optional<std::string_view> nextWakeupName;
+    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+        auto& callback = it->second;
+        if (!callback->wakeupTime()) {
+            continue;
+        }
+
+        if (it != skipUpdateIt) {
+            callback->update(mTracker, now);
+        }
+        auto const wakeupTime = *callback->wakeupTime();
+        if (!min || (min && *min > wakeupTime)) {
+            nextWakeupName = callback->name();
+            min = wakeupTime;
+            targetVsync = callback->targetVsync();
+        }
+    }
+
+    if (min && (min < mIntendedWakeupTime)) {
+        if (targetVsync && nextWakeupName) {
+            mTraceBuffer.note(*nextWakeupName, *min - now, *targetVsync - now);
+        }
+        setTimer(*min, now);
+    } else {
+        ATRACE_NAME("cancel timer");
+        cancelTimer();
+    }
+}
+
+void VSyncDispatchTimerQueue::timerCallback() {
+    struct Invocation {
+        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
+        nsecs_t vsyncTimestamp;
+        nsecs_t wakeupTimestamp;
+    };
+    std::vector<Invocation> invocations;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+            auto& callback = it->second;
+            auto const wakeupTime = callback->wakeupTime();
+            if (!wakeupTime) {
+                continue;
+            }
+
+            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack) {
+                callback->executing();
+                invocations.emplace_back(
+                        Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime});
+            }
+        }
+
+        mIntendedWakeupTime = kInvalidTime;
+        rearmTimer(mTimeKeeper->now());
+    }
+
+    for (auto const& invocation : invocations) {
+        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp);
+    }
+}
+
+VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
+        Callback const& callbackFn, std::string callbackName) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+    return CallbackToken{
+            mCallbacks
+                    .emplace(++mCallbackToken,
+                             std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
+                                                                            callbackFn,
+                                                                            mMinVsyncDistance))
+                    .first->first};
+}
+
+void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
+    std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        auto it = mCallbacks.find(token);
+        if (it != mCallbacks.end()) {
+            entry = it->second;
+            mCallbacks.erase(it);
+        }
+    }
+
+    if (entry) {
+        entry->ensureNotRunning();
+    }
+}
+
+ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
+                                                 nsecs_t earliestVsync) {
+    auto result = ScheduleResult::Error;
+    {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+        auto it = mCallbacks.find(token);
+        if (it == mCallbacks.end()) {
+            return result;
+        }
+        auto& callback = it->second;
+        auto const now = mTimeKeeper->now();
+        result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+        if (result == ScheduleResult::CannotSchedule) {
+            return result;
+        }
+
+        if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
+            rearmTimerSkippingUpdateFor(now, it);
+        }
+    }
+
+    return result;
+}
+
+CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
+    std::lock_guard<decltype(mMutex)> lk(mMutex);
+
+    auto it = mCallbacks.find(token);
+    if (it == mCallbacks.end()) {
+        return CancelResult::Error;
+    }
+    auto& callback = it->second;
+
+    if (callback->wakeupTime()) {
+        callback->disarm();
+        mIntendedWakeupTime = kInvalidTime;
+        rearmTimer(mTimeKeeper->now());
+        return CancelResult::Cancelled;
+    }
+    return CancelResult::TooLate;
+}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
+                                                     VSyncDispatch::Callback const& callbackFn,
+                                                     std::string const& callbackName)
+      : mDispatch(dispatch),
+        mToken(dispatch.registerCallback(callbackFn, callbackName)),
+        mValidToken(true) {}
+
+VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other)
+      : mDispatch(other.mDispatch),
+        mToken(std::move(other.mToken)),
+        mValidToken(std::move(other.mValidToken)) {
+    other.mValidToken = false;
+}
+
+VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackRegistration&& other) {
+    mDispatch = std::move(other.mDispatch);
+    mToken = std::move(other.mToken);
+    mValidToken = std::move(other.mValidToken);
+    other.mValidToken = false;
+    return *this;
+}
+
+VSyncCallbackRegistration::~VSyncCallbackRegistration() {
+    if (mValidToken) mDispatch.get().unregisterCallback(mToken);
+}
+
+ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
+    if (!mValidToken) {
+        return ScheduleResult::Error;
+    }
+    return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
+}
+
+CancelResult VSyncCallbackRegistration::cancel() {
+    if (!mValidToken) {
+        return CancelResult::Error;
+    }
+    return mDispatch.get().cancel(mToken);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
new file mode 100644
index 0000000..fd0a034
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -0,0 +1,154 @@
+/*
+ * 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 <array>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+#include "SchedulerUtils.h"
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+
+// VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in
+// VSyncDispatchTimerQueue hoisted to public for unit testing.
+class VSyncDispatchTimerQueueEntry {
+public:
+    // This is the state of the entry. There are 3 states, armed, running, disarmed.
+    // Valid transition: disarmed -> armed ( when scheduled )
+    // Valid transition: armed -> running -> disarmed ( when timer is called)
+    // Valid transition: armed -> disarmed ( when cancelled )
+    VSyncDispatchTimerQueueEntry(std::string const& name, VSyncDispatch::Callback const& fn,
+                                 nsecs_t minVsyncDistance);
+    std::string_view name() const;
+
+    // Start: functions that are not threadsafe.
+    // Return the last vsync time this callback was invoked.
+    std::optional<nsecs_t> lastExecutedVsyncTarget() const;
+
+    // This moves the state from disarmed->armed and will calculate the wakeupTime.
+    ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+                            nsecs_t now);
+    // This will update armed entries with the latest vsync information. Entry remains armed.
+    void update(VSyncTracker& tracker, nsecs_t now);
+
+    // This will return empty if not armed, or the next calculated wakeup time if armed.
+    // It will not update the wakeupTime.
+    std::optional<nsecs_t> wakeupTime() const;
+
+    std::optional<nsecs_t> targetVsync() const;
+
+    // This moves state from armed->disarmed.
+    void disarm();
+
+    // This moves the state from armed->running.
+    // Store the timestamp that this was intended for as the last called timestamp.
+    nsecs_t executing();
+    // End: functions that are not threadsafe.
+
+    // Invoke the callback with the two given timestamps, moving the state from running->disarmed.
+    void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp);
+    // Block calling thread while the callback is executing.
+    void ensureNotRunning();
+
+private:
+    std::string const mName;
+    VSyncDispatch::Callback const mCallback;
+
+    nsecs_t mWorkDuration;
+    nsecs_t mEarliestVsync;
+    nsecs_t const mMinVsyncDistance;
+
+    struct ArmingInfo {
+        nsecs_t mActualWakeupTime;
+        nsecs_t mActualVsyncTime;
+    };
+    std::optional<ArmingInfo> mArmedInfo;
+    std::optional<nsecs_t> mLastDispatchTime;
+
+    std::mutex mRunningMutex;
+    std::condition_variable mCv;
+    bool mRunning GUARDED_BY(mRunningMutex) = false;
+};
+
+/*
+ * VSyncDispatchTimerQueue is a class that will dispatch callbacks as per VSyncDispatch interface
+ * using a single timer queue.
+ */
+class VSyncDispatchTimerQueue : public VSyncDispatch {
+public:
+    // Constructs a VSyncDispatchTimerQueue.
+    // \param[in] tk                    A timekeeper.
+    // \param[in] tracker               A tracker.
+    // \param[in] timerSlack            The threshold at which different similarly timed callbacks
+    //                                  should be grouped into one wakeup.
+    // \param[in] minVsyncDistance      The minimum distance between two vsync estimates before the
+    //                                  vsyncs are considered the same vsync event.
+    explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
+                                     nsecs_t timerSlack, nsecs_t minVsyncDistance);
+    ~VSyncDispatchTimerQueue();
+
+    CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final;
+    void unregisterCallback(CallbackToken token) final;
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
+    CancelResult cancel(CallbackToken token) final;
+
+private:
+    VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
+    VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
+
+    using CallbackMap =
+            std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+
+    void timerCallback();
+    void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
+    void rearmTimer(nsecs_t now) REQUIRES(mMutex);
+    void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+            REQUIRES(mMutex);
+    void cancelTimer() REQUIRES(mMutex);
+
+    static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max();
+    std::unique_ptr<TimeKeeper> const mTimeKeeper;
+    VSyncTracker& mTracker;
+    nsecs_t const mTimerSlack;
+    nsecs_t const mMinVsyncDistance;
+
+    std::mutex mutable mMutex;
+    size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+
+    CallbackMap mCallbacks GUARDED_BY(mMutex);
+    nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
+
+    struct TraceBuffer {
+        static constexpr char const kTraceNamePrefix[] = "-alarm in:";
+        static constexpr char const kTraceNameSeparator[] = " for vs:";
+        static constexpr size_t kMaxNamePrint = 4;
+        static constexpr size_t kNumTsPrinted = 2;
+        static constexpr size_t maxlen = kMaxNamePrint + arrayLen(kTraceNamePrefix) +
+                arrayLen(kTraceNameSeparator) - 1 + (kNumTsPrinted * max64print);
+        std::array<char, maxlen> str_buffer;
+        void note(std::string_view name, nsecs_t in, nsecs_t vs);
+    } mTraceBuffer GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 7a3bf8e..40a992c 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
 #include "VSyncModulator.h"
@@ -24,25 +28,24 @@
 #include <cinttypes>
 #include <mutex>
 
-namespace android {
+namespace android::scheduler {
 
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-VSyncModulator::VSyncModulator() {
+VSyncModulator::VSyncModulator(Scheduler& scheduler,
+                               Scheduler::ConnectionHandle appConnectionHandle,
+                               Scheduler::ConnectionHandle sfConnectionHandle,
+                               const OffsetsConfig& config)
+      : mScheduler(scheduler),
+        mAppConnectionHandle(appConnectionHandle),
+        mSfConnectionHandle(sfConnectionHandle),
+        mOffsetsConfig(config) {
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.vsync_trace_detailed_info", value, "0");
     mTraceDetailedInfo = atoi(value);
-    // Populate the offset map with some default offsets.
-    const Offsets defaultOffsets = {RefreshRateType::DEFAULT, 0, 0};
-    setPhaseOffsets(defaultOffsets, defaultOffsets, defaultOffsets, 0);
 }
 
-void VSyncModulator::setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
-                                     nsecs_t thresholdForNextVsync) {
+void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
     std::lock_guard<std::mutex> lock(mMutex);
-    mOffsetMap.insert_or_assign(OffsetType::Early, early);
-    mOffsetMap.insert_or_assign(OffsetType::EarlyGl, earlyGl);
-    mOffsetMap.insert_or_assign(OffsetType::Late, late);
-    mThresholdForNextVsync = thresholdForNextVsync;
+    mOffsetsConfig = config;
     updateOffsetsLocked();
 }
 
@@ -100,25 +103,21 @@
     }
 }
 
-VSyncModulator::Offsets VSyncModulator::getOffsets() {
+VSyncModulator::Offsets VSyncModulator::getOffsets() const {
     std::lock_guard<std::mutex> lock(mMutex);
     return mOffsets;
 }
 
-VSyncModulator::Offsets VSyncModulator::getNextOffsets() {
-    return mOffsetMap.at(getNextOffsetType());
-}
-
-VSyncModulator::OffsetType VSyncModulator::getNextOffsetType() {
+const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const {
     // Early offsets are used if we're in the middle of a refresh rate
     // change, or if we recently begin a transaction.
     if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 ||
         mRefreshRateChangePending) {
-        return OffsetType::Early;
+        return mOffsetsConfig.early;
     } else if (mRemainingRenderEngineUsageCount > 0) {
-        return OffsetType::EarlyGl;
+        return mOffsetsConfig.earlyGl;
     } else {
-        return OffsetType::Late;
+        return mOffsetsConfig.late;
     }
 }
 
@@ -128,37 +127,27 @@
 }
 
 void VSyncModulator::updateOffsetsLocked() {
-    const Offsets desired = getNextOffsets();
+    const Offsets& offsets = getNextOffsets();
 
-    if (mSfConnectionHandle != nullptr) {
-        mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf);
-    }
+    mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf);
+    mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app);
 
-    if (mAppConnectionHandle != nullptr) {
-        mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app);
-    }
+    mOffsets = offsets;
 
-    flushOffsets();
-}
-
-void VSyncModulator::flushOffsets() {
-    OffsetType type = getNextOffsetType();
-    mOffsets = mOffsetMap.at(type);
     if (!mTraceDetailedInfo) {
         return;
     }
-    ATRACE_INT("Vsync-EarlyOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Early);
-    ATRACE_INT("Vsync-EarlyGLOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::EarlyGl);
-    ATRACE_INT("Vsync-LateOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Late);
-    ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Early);
-    ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::EarlyGl);
-    ATRACE_INT("Vsync-HighFpsLateOffsetsOn",
-               mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Late);
+
+    const bool isEarly = &offsets == &mOffsetsConfig.early;
+    const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
+    const bool isLate = &offsets == &mOffsetsConfig.late;
+
+    ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+    ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
+    ATRACE_INT("Vsync-LateOffsetsOn", isLate);
 }
 
-} // namespace android
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index ddbd221..704a5d5 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -16,12 +16,11 @@
 
 #pragma once
 
-#include <cinttypes>
 #include <mutex>
 
 #include "Scheduler.h"
 
-namespace android {
+namespace android::scheduler {
 
 /*
  * Modulates the vsync-offsets depending on current SurfaceFlinger state.
@@ -31,51 +30,41 @@
     // Number of frames we'll keep the early phase offsets once they are activated for a
     // transaction. 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_TRANSACTION = 2;
+    static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2;
 
     // Number of frames we'll keep the early gl phase offsets once they are activated.
     // This acts as a low-pass filter to avoid scenarios where we rapidly
     // switch in and out of gl composition.
-    const int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
+    static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
 
 public:
-    VSyncModulator();
-
     // Wrapper for a collection of surfaceflinger/app offsets for a particular
-    // configuration .
+    // configuration.
     struct Offsets {
-        scheduler::RefreshRateConfigs::RefreshRateType fpsMode;
         nsecs_t sf;
         nsecs_t app;
+
+        bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
+
+        bool operator!=(const Offsets& other) const { return !(*this == other); }
     };
 
-    enum class OffsetType {
-        Early,
-        EarlyGl,
-        Late,
+    struct OffsetsConfig {
+        Offsets early;   // For transactions with the eEarlyWakeup flag.
+        Offsets earlyGl; // As above but while compositing with GL.
+        Offsets late;    // Default.
+
+        bool operator==(const OffsetsConfig& other) const {
+            return early == other.early && earlyGl == other.earlyGl && late == other.late;
+        }
+
+        bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
     };
 
-    // Sets the phase offsets
-    //
-    // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction
-    //          as early. May be the same as late, in which case we don't shift offsets.
-    // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition
-    //            and the transaction was marked as early, we'll use sfEarly.
-    // sfLate: The regular SF vsync phase offset.
-    // appEarly: Like sfEarly, but for the app-vsync
-    // appEarlyGl: Like sfEarlyGl, but for the app-vsync.
-    // appLate: The regular app vsync phase offset.
-    void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late,
-                         nsecs_t thresholdForNextVsync) EXCLUDES(mMutex);
+    VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
+                   ConnectionHandle sfConnectionHandle, const OffsetsConfig&);
 
-    // Sets the scheduler and vsync connection handlers.
-    void setSchedulerAndHandles(Scheduler* scheduler,
-                                Scheduler::ConnectionHandle* appConnectionHandle,
-                                Scheduler::ConnectionHandle* sfConnectionHandle) {
-        mScheduler = scheduler;
-        mAppConnectionHandle = appConnectionHandle;
-        mSfConnectionHandle = sfConnectionHandle;
-    }
+    void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex);
 
     // Signals that a transaction has started, and changes offsets accordingly.
     void setTransactionStart(Scheduler::TransactionStart transactionStart);
@@ -98,28 +87,23 @@
     void onRefreshed(bool usedRenderEngine);
 
     // Returns the offsets that we are currently using
-    Offsets getOffsets() EXCLUDES(mMutex);
+    Offsets getOffsets() const EXCLUDES(mMutex);
 
 private:
     // Returns the next offsets that we should be using
-    Offsets getNextOffsets() REQUIRES(mMutex);
-    // Returns the next offset type that we should use.
-    OffsetType getNextOffsetType();
+    const Offsets& getNextOffsets() const REQUIRES(mMutex);
     // Updates offsets and persists them into the scheduler framework.
     void updateOffsets() EXCLUDES(mMutex);
     void updateOffsetsLocked() REQUIRES(mMutex);
-    // Updates the internal offsets and offset type.
-    void flushOffsets() REQUIRES(mMutex);
+
+    Scheduler& mScheduler;
+    const ConnectionHandle mAppConnectionHandle;
+    const ConnectionHandle mSfConnectionHandle;
 
     mutable std::mutex mMutex;
-    std::unordered_map<OffsetType, Offsets> mOffsetMap GUARDED_BY(mMutex);
-    nsecs_t mThresholdForNextVsync;
+    OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex);
 
-    Scheduler* mScheduler = nullptr;
-    Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr;
-    Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr;
-
-    Offsets mOffsets GUARDED_BY(mMutex) = {Scheduler::RefreshRateType::DEFAULT, 0, 0};
+    Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late};
 
     std::atomic<Scheduler::TransactionStart> mTransactionStart =
             Scheduler::TransactionStart::NORMAL;
@@ -130,4 +114,4 @@
     bool mTraceDetailedInfo = false;
 };
 
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
new file mode 100644
index 0000000..399da19
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+//#define LOG_NDEBUG 0
+#include "VSyncPredictor.h"
+#include <android-base/logging.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <algorithm>
+#include <chrono>
+#include <sstream>
+
+namespace android::scheduler {
+
+static auto constexpr kMaxPercent = 100u;
+
+VSyncPredictor::~VSyncPredictor() = default;
+
+VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize,
+                               size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent)
+      : mTraceOn(property_get_bool("debug.sf.vsp_trace", true)),
+        kHistorySize(historySize),
+        kMinimumSamplesForPrediction(minimumSamplesForPrediction),
+        kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
+        mIdealPeriod(idealPeriod) {
+    resetModel();
+}
+
+inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const {
+    if (CC_UNLIKELY(mTraceOn)) {
+        ATRACE_INT64(name, value);
+    }
+}
+
+inline size_t VSyncPredictor::next(int i) const {
+    return (i + 1) % mTimestamps.size();
+}
+
+bool VSyncPredictor::validate(nsecs_t timestamp) const {
+    if (mLastTimestampIndex < 0 || mTimestamps.empty()) {
+        return true;
+    }
+
+    auto const aValidTimestamp = mTimestamps[mLastTimestampIndex];
+    auto const percent = (timestamp - aValidTimestamp) % mIdealPeriod * kMaxPercent / mIdealPeriod;
+    return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
+}
+
+nsecs_t VSyncPredictor::currentPeriod() const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+}
+
+bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
+    std::lock_guard<std::mutex> lk(mMutex);
+
+    if (!validate(timestamp)) {
+        ALOGV("timestamp was too far off the last known timestamp");
+        return false;
+    }
+
+    if (mTimestamps.size() != kHistorySize) {
+        mTimestamps.push_back(timestamp);
+        mLastTimestampIndex = next(mLastTimestampIndex);
+    } else {
+        mLastTimestampIndex = next(mLastTimestampIndex);
+        mTimestamps[mLastTimestampIndex] = timestamp;
+    }
+
+    if (mTimestamps.size() < kMinimumSamplesForPrediction) {
+        mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+        return true;
+    }
+
+    // This is a 'simple linear regression' calculation of Y over X, with Y being the
+    // vsync timestamps, and X being the ordinal of vsync count.
+    // The calculated slope is the vsync period.
+    // Formula for reference:
+    // Sigma_i: means sum over all timestamps.
+    // mean(variable): statistical mean of variable.
+    // X: snapped ordinal of the timestamp
+    // Y: vsync timestamp
+    //
+    //         Sigma_i( (X_i - mean(X)) * (Y_i - mean(Y) )
+    // slope = -------------------------------------------
+    //         Sigma_i ( X_i - mean(X) ) ^ 2
+    //
+    // intercept = mean(Y) - slope * mean(X)
+    //
+    std::vector<nsecs_t> vsyncTS(mTimestamps.size());
+    std::vector<nsecs_t> ordinals(mTimestamps.size());
+
+    // normalizing to the oldest timestamp cuts down on error in calculating the intercept.
+    auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end());
+    auto it = mRateMap.find(mIdealPeriod);
+    auto const currentPeriod = std::get<0>(it->second);
+    // TODO (b/144707443): its important that there's some precision in the mean of the ordinals
+    //                     for the intercept calculation, so scale the ordinals by 10 to continue
+    //                     fixed point calculation. Explore expanding
+    //                     scheduler::utils::calculate_mean to have a fixed point fractional part.
+    static constexpr int kScalingFactor = 10;
+
+    for (auto i = 0u; i < mTimestamps.size(); i++) {
+        traceInt64If("VSP-ts", mTimestamps[i]);
+
+        vsyncTS[i] = mTimestamps[i] - oldest_ts;
+        ordinals[i] = ((vsyncTS[i] + (currentPeriod / 2)) / currentPeriod) * kScalingFactor;
+    }
+
+    auto meanTS = scheduler::calculate_mean(vsyncTS);
+    auto meanOrdinal = scheduler::calculate_mean(ordinals);
+    for (auto i = 0; i < vsyncTS.size(); i++) {
+        vsyncTS[i] -= meanTS;
+        ordinals[i] -= meanOrdinal;
+    }
+
+    auto top = 0ll;
+    auto bottom = 0ll;
+    for (auto i = 0; i < vsyncTS.size(); i++) {
+        top += vsyncTS[i] * ordinals[i];
+        bottom += ordinals[i] * ordinals[i];
+    }
+
+    if (CC_UNLIKELY(bottom == 0)) {
+        it->second = {mIdealPeriod, 0};
+        clearTimestamps();
+        return false;
+    }
+
+    nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
+    nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
+
+    auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
+    if (percent >= kOutlierTolerancePercent) {
+        it->second = {mIdealPeriod, 0};
+        clearTimestamps();
+        return false;
+    }
+
+    traceInt64If("VSP-period", anticipatedPeriod);
+    traceInt64If("VSP-intercept", intercept);
+
+    it->second = {anticipatedPeriod, intercept};
+
+    ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
+          anticipatedPeriod, intercept);
+    return true;
+}
+
+nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
+    std::lock_guard<std::mutex> lk(mMutex);
+
+    auto const [slope, intercept] = getVSyncPredictionModel(lk);
+
+    if (mTimestamps.empty()) {
+        traceInt64If("VSP-mode", 1);
+        auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
+        auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
+        return knownTimestamp + numPeriodsOut * mIdealPeriod;
+    }
+
+    auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
+
+    // See b/145667109, the ordinal calculation must take into account the intercept.
+    auto const zeroPoint = oldest + intercept;
+    auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
+    auto const prediction = (ordinalRequest * slope) + intercept + oldest;
+
+    traceInt64If("VSP-mode", 0);
+    traceInt64If("VSP-timePoint", timePoint);
+    traceInt64If("VSP-prediction", prediction);
+
+    auto const printer = [&, slope = slope, intercept = intercept] {
+        std::stringstream str;
+        str << "prediction made from: " << timePoint << "prediction: " << prediction << " (+"
+            << prediction - timePoint << ") slope: " << slope << " intercept: " << intercept
+            << "oldestTS: " << oldest << " ordinal: " << ordinalRequest;
+        return str.str();
+    };
+
+    ALOGV("%s", printer().c_str());
+    LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
+                        printer().c_str());
+
+    return prediction;
+}
+
+std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const {
+    std::lock_guard<std::mutex> lk(mMutex);
+    return VSyncPredictor::getVSyncPredictionModel(lk);
+}
+
+std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel(
+        std::lock_guard<std::mutex> const&) const {
+    return mRateMap.find(mIdealPeriod)->second;
+}
+
+void VSyncPredictor::setPeriod(nsecs_t period) {
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    static constexpr size_t kSizeLimit = 30;
+    if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) {
+        mRateMap.erase(mRateMap.begin());
+    }
+
+    mIdealPeriod = period;
+    if (mRateMap.find(period) == mRateMap.end()) {
+        mRateMap[mIdealPeriod] = {period, 0};
+    }
+
+    clearTimestamps();
+}
+
+void VSyncPredictor::clearTimestamps() {
+    if (!mTimestamps.empty()) {
+        mKnownTimestamp = *std::max_element(mTimestamps.begin(), mTimestamps.end());
+        mTimestamps.clear();
+        mLastTimestampIndex = 0;
+    }
+}
+
+bool VSyncPredictor::needsMoreSamples(nsecs_t now) const {
+    using namespace std::literals::chrono_literals;
+    std::lock_guard<std::mutex> lk(mMutex);
+    bool needsMoreSamples = true;
+    if (mTimestamps.size() >= kMinimumSamplesForPrediction) {
+        nsecs_t constexpr aLongTime =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(500ms).count();
+        if (!(mLastTimestampIndex < 0 || mTimestamps.empty())) {
+            auto const lastTimestamp = mTimestamps[mLastTimestampIndex];
+            needsMoreSamples = !((lastTimestamp + aLongTime) > now);
+        }
+    }
+
+    ATRACE_INT("VSP-moreSamples", needsMoreSamples);
+    return needsMoreSamples;
+}
+
+void VSyncPredictor::resetModel() {
+    std::lock_guard<std::mutex> lk(mMutex);
+    mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
+    clearTimestamps();
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
new file mode 100644
index 0000000..ef1d88a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.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 <android-base/thread_annotations.h>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+#include "SchedulerUtils.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+class VSyncPredictor : public VSyncTracker {
+public:
+    /*
+     * \param [in] idealPeriod  The initial ideal period to use.
+     * \param [in] historySize  The internal amount of entries to store in the model.
+     * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before
+     * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter
+     * samples that fall outlierTolerancePercent from an anticipated vsync event.
+     */
+    VSyncPredictor(nsecs_t idealPeriod, size_t historySize, size_t minimumSamplesForPrediction,
+                   uint32_t outlierTolerancePercent);
+    ~VSyncPredictor();
+
+    bool addVsyncTimestamp(nsecs_t timestamp) final;
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
+    nsecs_t currentPeriod() const final;
+    void resetModel() final;
+
+    /*
+     * Inform the model that the period is anticipated to change to a new value.
+     * model will use the period parameter to predict vsync events until enough
+     * timestamps with the new period have been collected.
+     *
+     * \param [in] period   The new period that should be used.
+     */
+    void setPeriod(nsecs_t period) final;
+
+    /* Query if the model is in need of more samples to make a prediction at timePoint.
+     * \param [in] timePoint    The timePoint to inquire of.
+     * \return  True, if model would benefit from more samples, False if not.
+     */
+    bool needsMoreSamples(nsecs_t timePoint) const;
+
+    std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+
+private:
+    VSyncPredictor(VSyncPredictor const&) = delete;
+    VSyncPredictor& operator=(VSyncPredictor const&) = delete;
+    void clearTimestamps() REQUIRES(mMutex);
+
+    inline void traceInt64If(const char* name, int64_t value) const;
+    bool const mTraceOn;
+
+    size_t const kHistorySize;
+    size_t const kMinimumSamplesForPrediction;
+    size_t const kOutlierTolerancePercent;
+
+    std::mutex mutable mMutex;
+    size_t next(int i) const REQUIRES(mMutex);
+    bool validate(nsecs_t timestamp) const REQUIRES(mMutex);
+    std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const
+            REQUIRES(mMutex);
+
+    nsecs_t mIdealPeriod GUARDED_BY(mMutex);
+    std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex);
+
+    std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex);
+
+    int mLastTimestampIndex GUARDED_BY(mMutex) = 0;
+    std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
new file mode 100644
index 0000000..949ba4c
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -0,0 +1,332 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+#undef LOG_TAG
+#define LOG_TAG "VSyncReactor"
+//#define LOG_NDEBUG 0
+#include "VSyncReactor.h"
+#include <log/log.h>
+#include <utils/Trace.h>
+#include "TimeKeeper.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+Clock::~Clock() = default;
+nsecs_t SystemClock::now() const {
+    return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+                           std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
+      : mClock(std::move(clock)),
+        mTracker(std::move(tracker)),
+        mDispatch(std::move(dispatch)),
+        mPendingLimit(pendingFenceLimit) {}
+
+VSyncReactor::~VSyncReactor() = default;
+
+// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
+// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
+// for now.
+class CallbackRepeater {
+public:
+    CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
+                     nsecs_t period, nsecs_t offset, nsecs_t notBefore)
+          : mCallback(cb),
+            mRegistration(dispatch,
+                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
+                                    std::placeholders::_2),
+                          std::string(name)),
+            mPeriod(period),
+            mOffset(offset),
+            mLastCallTime(notBefore) {}
+
+    ~CallbackRepeater() {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mRegistration.cancel();
+    }
+
+    void start(nsecs_t offset) {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mStopped = false;
+        mOffset = offset;
+
+        auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
+        LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+                            "Error scheduling callback: rc %X", schedule_result);
+    }
+
+    void setPeriod(nsecs_t period) {
+        std::lock_guard<std::mutex> lk(mMutex);
+        if (period == mPeriod) {
+            return;
+        }
+        mPeriod = period;
+    }
+
+    void stop() {
+        std::lock_guard<std::mutex> lk(mMutex);
+        LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
+        mStopped = true;
+        mRegistration.cancel();
+    }
+
+private:
+    void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            mLastCallTime = vsynctime;
+        }
+
+        mCallback->onDispSyncEvent(wakeupTime);
+
+        {
+            std::lock_guard<std::mutex> lk(mMutex);
+            if (mStopped) {
+                return;
+            }
+            auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
+            LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+                                "Error rescheduling callback: rc %X", schedule_result);
+        }
+    }
+
+    // DispSync offsets are defined as time after the vsync before presentation.
+    // VSyncReactor workloads are defined as time before the intended presentation vsync.
+    // Note change in sign between the two defnitions.
+    nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+
+    DispSync::Callback* const mCallback;
+
+    std::mutex mutable mMutex;
+    VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+    bool mStopped GUARDED_BY(mMutex) = false;
+    nsecs_t mPeriod GUARDED_BY(mMutex);
+    nsecs_t mOffset GUARDED_BY(mMutex);
+    nsecs_t mLastCallTime GUARDED_BY(mMutex);
+};
+
+bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+    if (!fence) {
+        return false;
+    }
+
+    nsecs_t const signalTime = fence->getCachedSignalTime();
+    if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+        return true;
+    }
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (mExternalIgnoreFences || mInternalIgnoreFences) {
+        return true;
+    }
+
+    bool timestampAccepted = true;
+    for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
+        auto const time = (*it)->getCachedSignalTime();
+        if (time == Fence::SIGNAL_TIME_PENDING) {
+            it++;
+        } else if (time == Fence::SIGNAL_TIME_INVALID) {
+            it = mUnfiredFences.erase(it);
+        } else {
+            timestampAccepted &= mTracker->addVsyncTimestamp(time);
+
+            it = mUnfiredFences.erase(it);
+        }
+    }
+
+    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+        if (mPendingLimit == mUnfiredFences.size()) {
+            mUnfiredFences.erase(mUnfiredFences.begin());
+        }
+        mUnfiredFences.push_back(fence);
+    } else {
+        timestampAccepted &= mTracker->addVsyncTimestamp(signalTime);
+    }
+
+    if (!timestampAccepted) {
+        mMoreSamplesNeeded = true;
+        setIgnorePresentFencesInternal(true);
+        mPeriodConfirmationInProgress = true;
+    }
+
+    return mMoreSamplesNeeded;
+}
+
+void VSyncReactor::setIgnorePresentFences(bool ignoration) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    mExternalIgnoreFences = ignoration;
+    updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) {
+    mInternalIgnoreFences = ignoration;
+    updateIgnorePresentFencesInternal();
+}
+
+void VSyncReactor::updateIgnorePresentFencesInternal() {
+    if (mExternalIgnoreFences || mInternalIgnoreFences) {
+        mUnfiredFences.clear();
+    }
+}
+
+nsecs_t VSyncReactor::computeNextRefresh(int periodOffset) const {
+    auto const now = mClock->now();
+    auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
+    return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
+}
+
+nsecs_t VSyncReactor::expectedPresentTime() {
+    return mTracker->nextAnticipatedVSyncTimeFrom(mClock->now());
+}
+
+void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) {
+    mPeriodConfirmationInProgress = true;
+    mPeriodTransitioningTo = newPeriod;
+    mMoreSamplesNeeded = true;
+    setIgnorePresentFencesInternal(true);
+}
+
+void VSyncReactor::endPeriodTransition() {
+    setIgnorePresentFencesInternal(false);
+    mMoreSamplesNeeded = false;
+    mPeriodTransitioningTo.reset();
+    mPeriodConfirmationInProgress = false;
+    mLastHwVsync.reset();
+}
+
+void VSyncReactor::setPeriod(nsecs_t period) {
+    ATRACE_INT64("VSR-setPeriod", period);
+    std::lock_guard lk(mMutex);
+    mLastHwVsync.reset();
+    if (period == getPeriod()) {
+        endPeriodTransition();
+    } else {
+        startPeriodTransition(period);
+    }
+}
+
+nsecs_t VSyncReactor::getPeriod() {
+    return mTracker->currentPeriod();
+}
+
+void VSyncReactor::beginResync() {
+    mTracker->resetModel();
+}
+
+void VSyncReactor::endResync() {}
+
+bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) {
+    if (!mPeriodConfirmationInProgress) {
+        return false;
+    }
+
+    if (!mLastHwVsync && !HwcVsyncPeriod) {
+        return false;
+    }
+
+    auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod();
+    static constexpr int allowancePercent = 10;
+    static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio;
+    auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den;
+    if (HwcVsyncPeriod) {
+        return std::abs(*HwcVsyncPeriod - period) < allowance;
+    }
+
+    auto const distance = vsync_timestamp - *mLastHwVsync;
+    return std::abs(distance - period) < allowance;
+}
+
+bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                                   bool* periodFlushed) {
+    assert(periodFlushed);
+
+    std::lock_guard<std::mutex> lk(mMutex);
+    if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
+        if (mPeriodTransitioningTo) {
+            mTracker->setPeriod(*mPeriodTransitioningTo);
+            for (auto& entry : mCallbacks) {
+                entry.second->setPeriod(*mPeriodTransitioningTo);
+            }
+            *periodFlushed = true;
+        }
+        endPeriodTransition();
+    } else if (mPeriodConfirmationInProgress) {
+        mLastHwVsync = timestamp;
+        mMoreSamplesNeeded = true;
+        *periodFlushed = false;
+    } else {
+        mMoreSamplesNeeded = false;
+        *periodFlushed = false;
+    }
+
+    mTracker->addVsyncTimestamp(timestamp);
+    return mMoreSamplesNeeded;
+}
+
+status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
+                                        DispSync::Callback* callback,
+                                        nsecs_t /* lastCallbackTime */) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto it = mCallbacks.find(callback);
+    if (it == mCallbacks.end()) {
+        // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
+        static auto constexpr maxListeners = 3;
+        if (mCallbacks.size() >= maxListeners) {
+            ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
+                  maxListeners, mCallbacks.size());
+            return NO_MEMORY;
+        }
+
+        auto const period = mTracker->currentPeriod();
+        auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
+                                                           phase, mClock->now());
+        it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
+    }
+
+    it->second->start(phase);
+    return NO_ERROR;
+}
+
+status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
+                                           nsecs_t* /* outLastCallback */) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto const it = mCallbacks.find(callback);
+    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
+
+    it->second->stop();
+    return NO_ERROR;
+}
+
+status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+    std::lock_guard<std::mutex> lk(mMutex);
+    auto const it = mCallbacks.find(callback);
+    LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
+
+    it->second->start(phase);
+    return NO_ERROR;
+}
+
+void VSyncReactor::dump(std::string& result) const {
+    result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+}
+
+void VSyncReactor::reset() {}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
new file mode 100644
index 0000000..aa8a38d
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.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 <android-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+#include "DispSync.h"
+#include "TimeKeeper.h"
+namespace android::scheduler {
+
+class Clock;
+class VSyncDispatch;
+class VSyncTracker;
+class CallbackRepeater;
+
+// TODO (b/145217110): consider renaming.
+class VSyncReactor : public android::DispSync {
+public:
+    VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+                 std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+    ~VSyncReactor();
+
+    bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final;
+    void setIgnorePresentFences(bool ignoration) final;
+
+    nsecs_t computeNextRefresh(int periodOffset) const final;
+    nsecs_t expectedPresentTime() final;
+
+    void setPeriod(nsecs_t period) final;
+    nsecs_t getPeriod() final;
+
+    // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
+    void beginResync() final;
+    bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
+                         bool* periodFlushed) final;
+    void endResync() final;
+
+    status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
+                              nsecs_t lastCallbackTime) final;
+    status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) final;
+    status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) final;
+
+    void dump(std::string& result) const final;
+    void reset() final;
+
+private:
+    void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex);
+    void updateIgnorePresentFencesInternal() REQUIRES(mMutex);
+    void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex);
+    void endPeriodTransition() REQUIRES(mMutex);
+    bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod)
+            REQUIRES(mMutex);
+
+    std::unique_ptr<Clock> const mClock;
+    std::unique_ptr<VSyncTracker> const mTracker;
+    std::unique_ptr<VSyncDispatch> const mDispatch;
+    size_t const mPendingLimit;
+
+    std::mutex mMutex;
+    bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
+    bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
+    std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+
+    bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false;
+    bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false;
+    std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
+    std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
+
+    std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
+            GUARDED_BY(mMutex);
+};
+
+class SystemClock : public Clock {
+    nsecs_t now() const final;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
new file mode 100644
index 0000000..a25b8a9
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <utils/Timers.h>
+#include "VSyncDispatch.h"
+
+namespace android::scheduler {
+/*
+ * VSyncTracker is an interface for providing estimates on future Vsync signal times based on
+ * historical vsync timing data.
+ */
+class VSyncTracker {
+public:
+    virtual ~VSyncTracker();
+
+    /*
+     * Adds a known timestamp from a vsync timing source (HWVsync signal, present fence)
+     * to the model.
+     *
+     * \param [in] timestamp    The timestamp when the vsync signal was.
+     * \return                  True if the timestamp was consistent with the internal model,
+     *                          False otherwise
+     */
+    virtual bool addVsyncTimestamp(nsecs_t timestamp) = 0;
+
+    /*
+     * Access the next anticipated vsync time such that the anticipated time >= timePoint.
+     * This will always give the best accurate at the time of calling; multiple
+     * calls with the same timePoint might give differing values if the internal model
+     * is updated.
+     *
+     * \param [in] timePoint    The point in time after which to estimate a vsync event.
+     * \return                  A prediction of the timestamp of a vsync event.
+     */
+    virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+
+    /*
+     * The current period of the vsync signal.
+     *
+     * \return  The current period of the vsync signal
+     */
+    virtual nsecs_t currentPeriod() const = 0;
+
+    /*
+     * Inform the tracker that the period is changing and the tracker needs to recalibrate itself.
+     *
+     * \param [in] period   The period that the system is changing into.
+     */
+    virtual void setPeriod(nsecs_t period) = 0;
+
+    /* Inform the tracker that the samples it has are not accurate for prediction. */
+    virtual void resetModel() = 0;
+
+protected:
+    VSyncTracker(VSyncTracker const&) = delete;
+    VSyncTracker& operator=(VSyncTracker const&) = delete;
+    VSyncTracker() = default;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 7047710..a98ff4f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
@@ -38,14 +42,13 @@
 #include <binder/PermissionCache.h>
 
 #include <compositionengine/CompositionEngine.h>
+#include <compositionengine/CompositionRefreshArgs.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
-#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/RenderSurface.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputCompositionState.h>
-#include <compositionengine/impl/OutputLayerCompositionState.h>
 #include <dvr/vr_flinger.h>
 #include <gui/BufferQueue.h>
 #include <gui/DebugEGLImageTracker.h>
@@ -54,13 +57,16 @@
 #include <gui/IDisplayEventConnection.h>
 #include <gui/IProducerListener.h>
 #include <gui/LayerDebugInfo.h>
+#include <gui/LayerMetadata.h>
 #include <gui/Surface.h>
 #include <input/IInputFlinger.h>
 #include <renderengine/RenderEngine.h>
 #include <ui/ColorSpace.h>
 #include <ui/DebugUtils.h>
+#include <ui/DisplayConfig.h>
 #include <ui/DisplayInfo.h>
 #include <ui/DisplayStatInfo.h>
+#include <ui/DisplayState.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/PixelFormat.h>
 #include <ui/UiConfig.h>
@@ -78,10 +84,10 @@
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
 #include "Client.h"
-#include "ColorLayer.h"
 #include "Colorizer.h"
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
+#include "EffectLayer.h"
 #include "Layer.h"
 #include "LayerVector.h"
 #include "MonitoredProducer.h"
@@ -97,12 +103,12 @@
 #include "DisplayHardware/HWComposer.h"
 #include "DisplayHardware/VirtualDisplaySurface.h"
 #include "Effects/Daltonizer.h"
+#include "FrameTracer/FrameTracer.h"
 #include "RegionSamplingThread.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/PhaseOffsets.h"
 #include "Scheduler/Scheduler.h"
@@ -110,8 +116,10 @@
 
 #include <cutils/compiler.h>
 
+#include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 
+#include <android/configuration.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>
@@ -123,6 +131,8 @@
 
 namespace android {
 
+using namespace std::string_literals;
+
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 using namespace android::sysprop;
@@ -162,43 +172,6 @@
     return false;
 }
 
-bool isHdrColorMode(const ColorMode colorMode) {
-    switch (colorMode) {
-        case ColorMode::BT2100_PQ:
-        case ColorMode::BT2100_HLG:
-            return true;
-        case ColorMode::DISPLAY_P3:
-        case ColorMode::ADOBE_RGB:
-        case ColorMode::DCI_P3:
-        case ColorMode::BT2020:
-        case ColorMode::DISPLAY_BT2020:
-        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 {
@@ -214,6 +187,19 @@
     bool mLocked;
 };
 
+// TODO(b/141333600): Consolidate with HWC2::Display::Config::Builder::getDefaultDensity.
+constexpr float FALLBACK_DENSITY = ACONFIGURATION_DENSITY_TV / 160.f;
+
+float getDensityFromProperty(const char* property, bool required) {
+    char value[PROPERTY_VALUE_MAX];
+    const float density = property_get(property, value, nullptr) > 0 ? std::atof(value) : 0.f;
+    if (!density && required) {
+        ALOGE("%s must be defined as a build property", property);
+        return FALLBACK_DENSITY;
+    }
+    return density / 160.f;
+}
+
 // 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;
@@ -236,13 +222,14 @@
 bool SurfaceFlinger::useVrFlinger;
 int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
 bool SurfaceFlinger::hasWideColorDisplay;
-int SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
+ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
 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;
+bool SurfaceFlinger::useFrameRateApi;
 
 std::string getHwcServiceName() {
     char value[PROPERTY_VALUE_MAX] = {};
@@ -260,11 +247,11 @@
 
 std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
     switch(displayColorSetting) {
-        case DisplayColorSetting::MANAGED:
+        case DisplayColorSetting::kManaged:
             return std::string("Managed");
-        case DisplayColorSetting::UNMANAGED:
+        case DisplayColorSetting::kUnmanaged:
             return std::string("Unmanaged");
-        case DisplayColorSetting::ENHANCED:
+        case DisplayColorSetting::kEnhanced:
             return std::string("Enhanced");
         default:
             return std::string("Unknown ") +
@@ -276,11 +263,13 @@
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
       : mFactory(factory),
-        mPhaseOffsets(mFactory.createPhaseOffsets()),
         mInterceptor(mFactory.createSurfaceInterceptor(this)),
-        mTimeStats(mFactory.createTimeStats()),
+        mTimeStats(std::make_shared<impl::TimeStats>()),
+        mFrameTracer(std::make_unique<FrameTracer>()),
         mEventQueue(mFactory.createMessageQueue()),
-        mCompositionEngine(mFactory.createCompositionEngine()) {}
+        mCompositionEngine(mFactory.createCompositionEngine()),
+        mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)),
+        mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {}
 
 SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
     ALOGI("SurfaceFlinger is starting");
@@ -318,23 +307,21 @@
 
     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;
+    using Values = SurfaceFlingerProperties::primary_display_orientation_values;
+    switch (primary_display_orientation(Values::ORIENTATION_0)) {
+        case Values::ORIENTATION_0:
             break;
-        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_180:
-            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180;
+        case Values::ORIENTATION_90:
+            internalDisplayOrientation = ui::ROTATION_90;
             break;
-        case SurfaceFlingerProperties::primary_display_orientation_values::ORIENTATION_270:
-            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270;
+        case Values::ORIENTATION_180:
+            internalDisplayOrientation = ui::ROTATION_180;
             break;
-        default:
-            SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
+        case Values::ORIENTATION_270:
+            internalDisplayOrientation = ui::ROTATION_270;
             break;
     }
-    ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
+    ALOGV("Internal Display Orientation: %s", toCString(internalDisplayOrientation));
 
     mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries();
 
@@ -371,24 +358,23 @@
     mLayerTripleBufferingDisabled = atoi(value);
     ALOGI_IF(mLayerTripleBufferingDisabled, "Disabling Triple Buffering");
 
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    bool supportsBlurs = atoi(value);
+    property_get("debug.sf.disableBlurs", value, "0");
+    bool disableBlurs = atoi(value);
+    mEnableBlurs = supportsBlurs && !disableBlurs;
+    ALOGI_IF(!mEnableBlurs, "Disabling blur effects. supported: %d, disabled: %d", supportsBlurs,
+             disableBlurs);
+
     const size_t defaultListSize = MAX_LAYERS;
     auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
     mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
 
-    mUseSmart90ForVideo = use_smart_90_for_video(false);
-    property_get("debug.sf.use_smart_90_for_video", value, "0");
-
-    int int_value = atoi(value);
-    if (int_value) {
-        mUseSmart90ForVideo = true;
-    }
-
     property_get("debug.sf.luma_sampling", value, "1");
     mLumaSampling = atoi(value);
 
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
+    property_get("debug.sf.disable_client_composition_cache", value, "0");
+    mDisableClientCompositionCache = atoi(value);
 
     // We should be reading 'persist.sys.sf.color_saturation' here
     // but since /data may be encrypted, we need to wait until after vold
@@ -403,6 +389,8 @@
         // for production purposes later on.
         setenv("TREBLE_TESTING_OVERRIDE", "true", true);
     }
+
+    useFrameRateApi = use_frame_rate_api(true);
 }
 
 void SurfaceFlinger::onFirstRef()
@@ -538,6 +526,9 @@
     const nsecs_t duration = now - mBootTime;
     ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
 
+    mFrameTracer->initialize();
+    mTimeStats->onBootFinished();
+
     // wait patiently for the window manager death
     const String16 name("window");
     mWindowManager = defaultServiceManager()->getService(name);
@@ -569,14 +560,9 @@
         readPersistentProperties();
         mBootStage = BootStage::FINISHED;
 
-        // set the refresh rate according to the policy
-        const auto& performanceRefreshRate =
-                mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE);
-
-        if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) {
-            setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
-        } else {
-            setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
+        if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
+            mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
+            mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate());
         }
     }));
 }
@@ -615,57 +601,29 @@
 void SurfaceFlinger::init() {
     ALOGI(  "SurfaceFlinger's main thread ready to run. "
             "Initializing graphics H/W...");
-
-    ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset());
-
     Mutex::Autolock _l(mStateLock);
-    // start the EventThread
-    mScheduler =
-            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
-                                         mRefreshRateConfigs);
-    auto resyncCallback =
-            mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
-    mAppConnectionHandle =
-            mScheduler->createConnection("app", mVsyncModulator.getOffsets().app,
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
-                                         resyncCallback,
-                                         impl::EventThread::InterceptVSyncsCallback());
-    mSfConnectionHandle =
-            mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf,
-                                         mPhaseOffsets->getOffsetThresholdForNextVsync(),
-                                         resyncCallback, [this](nsecs_t timestamp) {
-                                             mInterceptor->saveVSyncEvent(timestamp);
-                                         });
-
-    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
-    mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
-                                           mSfConnectionHandle.get());
-
-    mRegionSamplingThread =
-            new RegionSamplingThread(*this, *mScheduler,
-                                     RegionSamplingThread::EnvironmentTimingTunables());
 
     // Get a RenderEngine for the given display / config (can't fail)
-    int32_t renderEngineFeature = 0;
-    renderEngineFeature |= (useColorManagement ?
-                            renderengine::RenderEngine::USE_COLOR_MANAGEMENT : 0);
-    renderEngineFeature |= (useContextPriority ?
-                            renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0);
-    renderEngineFeature |=
-            (enable_protected_contents(false) ? renderengine::RenderEngine::ENABLE_PROTECTED_CONTEXT
-                                              : 0);
-
     // TODO(b/77156734): We need to stop casting and use HAL types when possible.
     // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
-    mCompositionEngine->setRenderEngine(
-            renderengine::RenderEngine::create(static_cast<int32_t>(defaultCompositionPixelFormat),
-                                               renderEngineFeature, maxFrameBufferAcquiredBuffers));
+    mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
+            renderengine::RenderEngineCreationArgs::Builder()
+                .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+                .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+                .setUseColorManagerment(useColorManagement)
+                .setEnableProtectedContext(enable_protected_contents(false))
+                .setPrecacheToneMapperShaderOnly(false)
+                .setSupportsBackgroundBlur(mEnableBlurs)
+                .setContextPriority(useContextPriority
+                        ? renderengine::RenderEngine::ContextPriority::HIGH
+                        : renderengine::RenderEngine::ContextPriority::MEDIUM)
+                .build()));
+    mCompositionEngine->setTimeStats(mTimeStats);
 
     LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
             "Starting with vr flinger active is not currently supported.");
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
-    mCompositionEngine->getHwComposer().registerCallback(this, getBE().mComposerSequenceId);
+    mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId);
     // Process any initial hotplug and resulting display changes.
     processDisplayHotplugEventsLocked();
     const auto display = getDefaultDisplayDeviceLocked();
@@ -703,7 +661,11 @@
     // set initial conditions (e.g. unblank default device)
     initializeDisplays();
 
-    getRenderEngine().primeCache();
+    char primeShaderCache[PROPERTY_VALUE_MAX];
+    property_get("service.sf.prime_shader_cache", primeShaderCache, "1");
+    if (atoi(primeShaderCache)) {
+        getRenderEngine().primeCache();
+    }
 
     // Inform native graphics APIs whether the present timestamp is supported:
 
@@ -715,37 +677,6 @@
         ALOGE("Run StartPropertySetThread failed!");
     }
 
-    mScheduler->setChangeRefreshRateCallback(
-            [this](RefreshRateType type, Scheduler::ConfigEvent event) {
-                Mutex::Autolock lock(mStateLock);
-                setRefreshRateTo(type, event);
-            });
-    mScheduler->setGetCurrentRefreshRateTypeCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        const auto display = getDefaultDisplayDeviceLocked();
-        if (!display) {
-            // If we don't have a default display the fallback to the default
-            // refresh rate type
-            return RefreshRateType::DEFAULT;
-        }
-
-        const int configId = display->getActiveConfig();
-        for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) {
-            if (refresh && refresh->configId == configId) {
-                return type;
-            }
-        }
-        // This should never happen, but just gracefully fallback to default.
-        return RefreshRateType::DEFAULT;
-    });
-    mScheduler->setGetVsyncPeriodCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        return getVsyncPeriod();
-    });
-
-    mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
-    mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
-
     ALOGV("Done initializing");
 }
 
@@ -819,8 +750,60 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState* state) {
+    if (!displayToken || !state) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    state->layerStack = display->getLayerStack();
+    state->orientation = display->getOrientation();
+
+    const Rect viewport = display->getViewport();
+    state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize();
+
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo* info) {
+    if (!displayToken || !info) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+
+    const auto display = getDisplayDeviceLocked(displayToken);
+    if (!display) {
+        return NAME_NOT_FOUND;
+    }
+
+    if (const auto connectionType = display->getConnectionType())
+        info->connectionType = *connectionType;
+    else {
+        return INVALID_OPERATION;
+    }
+
+    if (mEmulatedDisplayDensity) {
+        info->density = mEmulatedDisplayDensity;
+    } else {
+        info->density = info->connectionType == DisplayConnectionType::Internal
+                ? mInternalDisplayDensity
+                : FALLBACK_DENSITY;
+    }
+
+    info->secure = display->isSecure();
+
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken,
-                                           Vector<DisplayInfo>* configs) {
+                                           Vector<DisplayConfig>* configs) {
     if (!displayToken || !configs) {
         return BAD_VALUE;
     }
@@ -832,76 +815,42 @@
         return NAME_NOT_FOUND;
     }
 
-    // TODO: Not sure if display density should handled by SF any longer
-    class Density {
-        static float getDensityFromProperty(char const* propName) {
-            char property[PROPERTY_VALUE_MAX];
-            float density = 0.0f;
-            if (property_get(propName, property, nullptr) > 0) {
-                density = strtof(property, nullptr);
-            }
-            return density;
-        }
-    public:
-        static float getEmuDensity() {
-            return getDensityFromProperty("qemu.sf.lcd_density"); }
-        static float getBuildDensity()  {
-            return getDensityFromProperty("ro.sf.lcd_density"); }
-    };
+    const bool isInternal = (displayId == getInternalDisplayIdLocked());
 
     configs->clear();
 
     for (const auto& hwConfig : getHwComposer().getConfigs(*displayId)) {
-        DisplayInfo info = DisplayInfo();
+        DisplayConfig config;
 
-        float xdpi = hwConfig->getDpiX();
-        float ydpi = hwConfig->getDpiY();
+        auto width = hwConfig->getWidth();
+        auto height = hwConfig->getHeight();
 
-        info.w = hwConfig->getWidth();
-        info.h = hwConfig->getHeight();
-        // Default display viewport to display width and height
-        info.viewportW = info.w;
-        info.viewportH = info.h;
+        auto xDpi = hwConfig->getDpiX();
+        auto yDpi = hwConfig->getDpiY();
 
-        if (displayId == getInternalDisplayIdLocked()) {
-            // The density of the device is provided by a build property
-            float density = Density::getBuildDensity() / 160.0f;
-            if (density == 0) {
-                // the build doesn't provide a density -- this is wrong!
-                // use xdpi instead
-                ALOGE("ro.sf.lcd_density must be defined as a build property");
-                density = xdpi / 160.0f;
-            }
-            if (Density::getEmuDensity()) {
-                // if "qemu.sf.lcd_density" is specified, it overrides everything
-                xdpi = ydpi = density = Density::getEmuDensity();
-                density /= 160.0f;
-            }
-            info.density = density;
-
-            // TODO: this needs to go away (currently needed only by webkit)
-            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;
-            info.density = TV_DENSITY / 160.0f;
-            info.orientation = 0;
+        if (isInternal &&
+            (internalDisplayOrientation == ui::ROTATION_90 ||
+             internalDisplayOrientation == ui::ROTATION_270)) {
+            std::swap(width, height);
+            std::swap(xDpi, yDpi);
         }
 
-        info.xdpi = xdpi;
-        info.ydpi = ydpi;
-        info.fps = 1e9 / hwConfig->getVsyncPeriod();
-        const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId());
-        const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
-        info.appVsyncOffset = offset.late.app;
+        config.resolution = ui::Size(width, height);
+
+        if (mEmulatedDisplayDensity) {
+            config.xDpi = mEmulatedDisplayDensity;
+            config.yDpi = mEmulatedDisplayDensity;
+        } else {
+            config.xDpi = xDpi;
+            config.yDpi = yDpi;
+        }
+
+        const nsecs_t period = hwConfig->getVsyncPeriod();
+        config.refreshRate = 1e9f / period;
+
+        const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
+        config.appVsyncOffset = offsets.late.app;
+        config.sfVsyncOffset = offsets.late.sf;
 
         // This is how far in advance a buffer must be queued for
         // presentation at a given time.  If you want a buffer to appear
@@ -915,17 +864,9 @@
         //
         // We add an additional 1ms to allow for processing time and
         // differences between the ideal and actual refresh rate.
-        info.presentationDeadline = hwConfig->getVsyncPeriod() - offset.late.sf + 1000000;
+        config.presentationDeadline = period - config.sfVsyncOffset + 1000000;
 
-        // All non-virtual displays are currently considered secure.
-        info.secure = true;
-
-        if (displayId == getInternalDisplayIdLocked() &&
-            primaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
-            std::swap(info.w, info.h);
-        }
-
-        configs->push_back(info);
+        configs->push_back(config);
     }
 
     return NO_ERROR;
@@ -947,11 +888,20 @@
         return BAD_VALUE;
     }
 
-    return display->getActiveConfig();
+    if (display->isPrimary()) {
+        std::lock_guard<std::mutex> lock(mActiveConfigLock);
+        if (mDesiredActiveConfigChanged) {
+            return mDesiredActiveConfig.configId.value();
+        }
+    }
+
+    return display->getActiveConfig().value();
 }
 
 void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
     ATRACE_CALL();
+    auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+    ALOGV("setDesiredActiveConfig(%s)", refreshRate.name.c_str());
 
     // Don't check against the current mode yet. Worst case we set the desired
     // config twice. However event generation config might have changed so we need to update it
@@ -966,30 +916,48 @@
         repaintEverythingForHWC();
         // Start receiving vsync samples now, so that we can detect a period
         // switch.
-        mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+        mScheduler->resyncToHardwareVsync(true, refreshRate.vsyncPeriod);
         // As we called to set period, we will call to onRefreshRateChangeCompleted once
         // DispSync model is locked.
-        mVsyncModulator.onRefreshRateChangeInitiated();
-        mPhaseOffsets->setRefreshRateType(info.type);
-        const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-        mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                        mPhaseOffsets->getOffsetThresholdForNextVsync());
+        mVSyncModulator->onRefreshRateChangeInitiated();
+
+        mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+        mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
     }
     mDesiredActiveConfigChanged = true;
-    ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
 
     if (mRefreshRateOverlay) {
-        mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
+        mRefreshRateOverlay->changeRefreshRate(refreshRate);
     }
 }
 
 status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
     ATRACE_CALL();
 
-    std::vector<int32_t> allowedConfig;
-    allowedConfig.push_back(mode);
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
 
-    return setAllowedDisplayConfigs(displayToken, allowedConfig);
+    status_t result = NO_ERROR;
+
+    postMessageSync(new LambdaMessage([&]() {
+        const auto display = getDisplayDeviceLocked(displayToken);
+        if (!display) {
+            ALOGE("Attempt to set allowed display configs for invalid display token %p",
+                  displayToken.get());
+            result = BAD_VALUE;
+        } else if (display->isVirtual()) {
+            ALOGW("Attempt to set allowed display configs for virtual display");
+            result = BAD_VALUE;
+        } else {
+            HwcConfigIndexType config(mode);
+            const auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(config);
+            result = setDesiredDisplayConfigSpecsInternal(display, config, refreshRate.fps,
+                                                          refreshRate.fps);
+        }
+    }));
+
+    return result;
 }
 
 void SurfaceFlinger::setActiveConfigInternal() {
@@ -1001,19 +969,22 @@
     }
 
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
-    mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId);
-
+    mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
+    mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
     display->setActiveConfig(mUpcomingActiveConfig.configId);
 
-    mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
-    ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+    auto refreshRate =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
+    mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
+    ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
 
     if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
+        const nsecs_t vsyncPeriod =
+                mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId)
+                        .vsyncPeriod;
         mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                    mUpcomingActiveConfig.configId);
+                                    mUpcomingActiveConfig.configId, vsyncPeriod);
     }
 }
 
@@ -1021,17 +992,17 @@
     std::lock_guard<std::mutex> lock(mActiveConfigLock);
     mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
     mDesiredActiveConfigChanged = false;
-    ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged);
 
-    mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
-    mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
-    const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
-    mVsyncModulator.setPhaseOffsets(early, gl, late,
-                                    mPhaseOffsets->getOffsetThresholdForNextVsync());
+    auto const refreshRate =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+    mScheduler->resyncToHardwareVsync(true, refreshRate.vsyncPeriod);
+    mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+    mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
 }
 
 bool SurfaceFlinger::performSetActiveConfig() {
     ATRACE_CALL();
+    ALOGV("performSetActiveConfig");
     if (mCheckPendingFence) {
         if (previousFrameMissed()) {
             // fence has not signaled yet. wait for the next invalidate
@@ -1055,6 +1026,10 @@
         desiredActiveConfig = mDesiredActiveConfig;
     }
 
+    auto refreshRate =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig.configId);
+    ALOGV("performSetActiveConfig changing active config to %d(%s)", refreshRate.configId.value(),
+          refreshRate.name.c_str());
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
         // display is not valid or we are already in the requested mode
@@ -1075,12 +1050,26 @@
     const auto displayId = display->getId();
     LOG_ALWAYS_FATAL_IF(!displayId);
 
-    ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
-    getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+    ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.fps);
 
-    // we need to submit an empty frame to HWC to start the process
+    // TODO(b/142753666) use constrains
+    HWC2::VsyncPeriodChangeConstraints constraints;
+    constraints.desiredTimeNanos = systemTime();
+    constraints.seamlessRequired = false;
+
+    HWC2::VsyncPeriodChangeTimeline outTimeline;
+    auto status =
+            getHwComposer().setActiveConfigWithConstraints(*displayId,
+                                                           mUpcomingActiveConfig.configId.value(),
+                                                           constraints, &outTimeline);
+    if (status != NO_ERROR) {
+        LOG_ALWAYS_FATAL("setActiveConfigWithConstraints failed: %d", status);
+        return false;
+    }
+
+    mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+    // Scheduler will submit an empty frame to HWC if needed.
     mCheckPendingFence = true;
-    mEventQueue->invalidate();
     return false;
 }
 
@@ -1159,14 +1148,96 @@
             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);
+            display->getCompositionDisplay()->setColorProfile(
+                    compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN,
+                                                            RenderIntent::COLORIMETRIC,
+                                                            Dataspace::UNKNOWN});
         }
     }));
 
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+                                                      bool* outSupport) const {
+    Mutex::Autolock _l(mStateLock);
+
+    if (!displayToken) {
+        ALOGE("getAutoLowLatencyModeSupport() failed. Missing display token.");
+        return BAD_VALUE;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("getAutoLowLatencyModeSupport() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return NAME_NOT_FOUND;
+    }
+    *outSupport = getHwComposer().hasDisplayCapability(displayId,
+                                                       HWC2::DisplayCapability::AutoLowLatencyMode);
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
+    postMessageAsync(new LambdaMessage([=] { setAutoLowLatencyModeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on) {
+    if (!displayToken) {
+        ALOGE("setAutoLowLatencyMode() failed. Missing display token.");
+        return;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("setAutoLowLatencyMode() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return;
+    }
+
+    getHwComposer().setAutoLowLatencyMode(*displayId, on);
+}
+
+status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
+                                                   bool* outSupport) const {
+    Mutex::Autolock _l(mStateLock);
+
+    if (!displayToken) {
+        ALOGE("getGameContentTypeSupport() failed. Missing display token.");
+        return BAD_VALUE;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("getGameContentTypeSupport() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return NAME_NOT_FOUND;
+    }
+
+    std::vector<HWC2::ContentType> outSupportedContentTypes;
+    getHwComposer().getSupportedContentTypes(*displayId, &outSupportedContentTypes);
+    *outSupport = std::find(outSupportedContentTypes.begin(), outSupportedContentTypes.end(),
+                            HWC2::ContentType::Game) != outSupportedContentTypes.end();
+    return NO_ERROR;
+}
+
+void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
+    postMessageAsync(new LambdaMessage([=] { setGameContentTypeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on) {
+    if (!displayToken) {
+        ALOGE("setGameContentType() failed. Missing display token.");
+        return;
+    }
+    const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+    if (!displayId) {
+        ALOGE("setGameContentType() failed. Display id for display token %p not found.",
+              displayToken.get());
+        return;
+    }
+
+    const HWC2::ContentType type = on ? HWC2::ContentType::Game : HWC2::ContentType::None;
+    getHwComposer().setContentType(*displayId, type);
+}
+
 status_t SurfaceFlinger::clearAnimationFrameStats() {
     Mutex::Autolock _l(mStateLock);
     mAnimFrameTracker.clearStats();
@@ -1274,51 +1345,20 @@
 
 status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
     postMessageSync(new LambdaMessage([&] {
-        Mutex::Autolock _l(mStateLock);
+        Mutex::Autolock lock(mStateLock);
 
-        if (mInjectVSyncs == enable) {
-            return;
+        if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
+            mEventQueue->setEventConnection(
+                    mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle));
         }
-
-        auto resyncCallback =
-                mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this));
-
-        // TODO(b/128863962): 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(),
-                                           impl::EventThread::InterceptVSyncsCallback(),
-                                           "injEventThread");
-            }
-            mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback));
-        } else {
-            ALOGV("VSync Injections disabled");
-            mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle),
-                                        std::move(resyncCallback));
-        }
-
-        mInjectVSyncs = enable;
     }));
 
     return NO_ERROR;
 }
 
 status_t SurfaceFlinger::injectVSync(nsecs_t when) {
-    Mutex::Autolock _l(mStateLock);
-
-    if (!mInjectVSyncs) {
-        ALOGE("VSync Injections not enabled");
-        return BAD_VALUE;
-    }
-    if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) {
-        ALOGV("Injecting VSync inside SurfaceFlinger");
-        mVSyncInjector->onInjectSyncEvent(when);
-    }
-    return NO_ERROR;
+    Mutex::Autolock lock(mStateLock);
+    return mScheduler->injectVSync(when) ? NO_ERROR : BAD_VALUE;
 }
 
 status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const
@@ -1409,16 +1449,10 @@
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
         ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) {
-    auto resyncCallback = mScheduler->makeResyncCallback([this] {
-        Mutex::Autolock lock(mStateLock);
-        return getVsyncPeriod();
-    });
-
     const auto& handle =
             vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle;
 
-    return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback),
-                                                    configChanged);
+    return mScheduler->createDisplayEventConnection(handle, configChanged);
 }
 
 // ----------------------------------------------------------------------------
@@ -1468,12 +1502,12 @@
         return 0;
     }
 
-    const auto config = getHwComposer().getActiveConfig(*displayId);
-    return config ? config->getVsyncPeriod() : 0;
+    return getHwComposer().getDisplayVsyncPeriod(*displayId);
 }
 
 void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
-                                     int64_t timestamp) {
+                                     int64_t timestamp,
+                                     std::optional<hwc2_vsync_period_t> vsyncPeriod) {
     ATRACE_NAME("SF onVsync");
 
     Mutex::Autolock lock(mStateLock);
@@ -1492,9 +1526,9 @@
     }
 
     bool periodFlushed = false;
-    mScheduler->addResyncSample(timestamp, &periodFlushed);
+    mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
     if (periodFlushed) {
-        mVsyncModulator.onRefreshRateChangeCompleted();
+        mVSyncModulator->onRefreshRateChangeCompleted();
     }
 }
 
@@ -1503,11 +1537,12 @@
     *compositorTiming = getBE().mCompositorTiming;
 }
 
-bool SurfaceFlinger::isDisplayConfigAllowed(int32_t configId) {
-    return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId);
+bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
+    return mRefreshRateConfigs->isConfigAllowed(configId);
 }
 
-void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
+void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
+                                             Scheduler::ConfigEvent event) {
     const auto display = getDefaultDisplayDeviceLocked();
     if (!display || mBootStage != BootStage::FINISHED) {
         return;
@@ -1515,20 +1550,13 @@
     ATRACE_CALL();
 
     // Don't do any updating if the current fps is the same as the new one.
-    const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate);
-    if (!refreshRateConfig) {
-        ALOGV("Skipping refresh rate change request for unsupported rate.");
+    if (!isDisplayConfigAllowed(refreshRate.configId)) {
+        ALOGV("Skipping config %d as it is not part of allowed configs",
+              refreshRate.configId.value());
         return;
     }
 
-    const int desiredConfigId = refreshRateConfig->configId;
-
-    if (!isDisplayConfigAllowed(desiredConfigId)) {
-        ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
-        return;
-    }
-
-    setDesiredActiveConfig({refreshRate, desiredConfigId, event});
+    setDesiredActiveConfig({refreshRate.configId, event});
 }
 
 void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1557,6 +1585,21 @@
     setTransactionFlags(eDisplayTransactionNeeded);
 }
 
+void SurfaceFlinger::onVsyncPeriodTimingChangedReceived(
+        int32_t sequenceId, hwc2_display_t /*display*/,
+        const hwc_vsync_period_change_timeline_t& updatedTimeline) {
+    Mutex::Autolock lock(mStateLock);
+    if (sequenceId != getBE().mComposerSequenceId) {
+        return;
+    }
+    mScheduler->onNewVsyncPeriodChangeTimeline(updatedTimeline);
+}
+
+void SurfaceFlinger::onSeamlessPossible(int32_t /*sequenceId*/, hwc2_display_t /*display*/) {
+    // TODO(b/142753666): use constraints when calling to setActiveConfigWithConstrains and
+    // use this callback to know when to retry in case of SEAMLESS_NOT_POSSIBLE.
+}
+
 void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
     Mutex::Autolock lock(mStateLock);
     if (sequenceId != getBE().mComposerSequenceId) {
@@ -1627,8 +1670,7 @@
     // any HWC layers are destroyed through that interface before it becomes
     // invalid.
     for (const auto& [token, displayDevice] : mDisplays) {
-        displayDevice->getCompositionDisplay()->setOutputLayersOrderedByZ(
-                compositionengine::Output::OutputLayers());
+        displayDevice->getCompositionDisplay()->clearOutputLayers();
     }
 
     // This DisplayDevice will no longer be relevant once resetDisplayState() is
@@ -1645,7 +1687,7 @@
     mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
     mCompositionEngine->setHwComposer(getFactory().createHWComposer(
             vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName));
-    getHwComposer().registerCallback(this, ++getBE().mComposerSequenceId);
+    mCompositionEngine->getHwComposer().setConfiguration(this, ++getBE().mComposerSequenceId);
 
     LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(),
                         "Switched to non-remote hardware composer");
@@ -1686,10 +1728,8 @@
     // We are storing the last 2 present fences. If sf's phase offset is to be
     // woken up before the actual vsync but targeting the next vsync, we need to check
     // fence N-2
-    const sp<Fence>& fence =
-            mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
-            ? mPreviousPresentFences[0]
-            : mPreviousPresentFences[1];
+    const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+                                                                  : mPreviousPresentFences[1];
 
     if (fence == Fence::NO_FENCE) {
         return false;
@@ -1702,21 +1742,20 @@
     return (fence->getStatus() == Fence::Status::Unsignaled);
 }
 
-void SurfaceFlinger::populateExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::populateExpectedPresentTime() {
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
     const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
     // Inflate the expected present time if we're targetting the next vsync.
-    mExpectedPresentTime =
-            mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
-            ? presentTime
-            : presentTime + stats.vsyncPeriod;
+    mExpectedPresentTime.store(
+            mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod);
 }
 
 void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
     ATRACE_CALL();
     switch (what) {
         case MessageQueue::INVALIDATE: {
+            const nsecs_t frameStart = systemTime();
             // calculate the expected present time once and use the cached
             // value throughout this frame to make sure all layers are
             // seeing this same value.
@@ -1730,12 +1769,14 @@
                      (mPropagateBackpressureClientComposition || !mHadClientComposition))
                     ? 1
                     : 0;
-            bool frameMissed = previousFrameMissed(graceTimeForPresentFenceMs);
-            bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
-            bool gpuFrameMissed = mHadClientComposition && frameMissed;
-            ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
-            ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed));
-            ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed));
+            const TracedOrdinal<bool> frameMissed = {"FrameMissed",
+                                                     previousFrameMissed(
+                                                             graceTimeForPresentFenceMs)};
+            const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
+                                                        mHadDeviceComposition && frameMissed};
+            const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
+                                                        mHadClientComposition && frameMissed};
+
             if (frameMissed) {
                 mFrameMissedCount++;
                 mTimeStats->incrementMissedFrames();
@@ -1749,16 +1790,6 @@
                 mGpuFrameMissedCount++;
             }
 
-            if (mUseSmart90ForVideo) {
-                // This call is made each time SF wakes up and creates a new frame. It is part
-                // of video detection feature.
-                mScheduler->updateFpsBasedOnContent();
-            }
-
-            if (performSetActiveConfig()) {
-                break;
-            }
-
             if (frameMissed && mPropagateBackpressure) {
                 if ((hwcFrameMissed && !gpuFrameMissed) ||
                     mPropagateBackpressureClientComposition) {
@@ -1775,6 +1806,19 @@
             bool refreshNeeded = handleMessageTransaction();
             refreshNeeded |= handleMessageInvalidate();
 
+            // Layers need to get updated (in the previous line) before we can use them for
+            // choosing the refresh rate.
+            // Hold mStateLock as chooseRefreshRateForContent promotes wp<Layer> to sp<Layer>
+            // and may eventually call to ~Layer() if it holds the last reference
+            {
+                Mutex::Autolock _l(mStateLock);
+                mScheduler->chooseRefreshRateForContent();
+            }
+
+            if (performSetActiveConfig()) {
+                break;
+            }
+
             updateCursorAsync();
             updateInputFlinger();
 
@@ -1783,6 +1827,13 @@
                 // Signal a refresh if a transaction modified the window state,
                 // a new buffer was latched, or if HWC has requested a full
                 // repaint
+                if (mFrameStartTime <= 0) {
+                    // We should only use the time of the first invalidate
+                    // message that signals a refresh as the beginning of the
+                    // frame. Otherwise the real frame time will be
+                    // underestimated.
+                    mFrameStartTime = frameStart;
+                }
                 signalRefresh();
             }
             break;
@@ -1821,43 +1872,89 @@
 
     mRefreshPending = false;
 
-    const bool repaintEverything = mRepaintEverything.exchange(false);
-    preComposition();
-    rebuildLayerStacks();
-    calculateWorkingSet();
-    long compositionTime = elapsedRealtimeNano();
-    for (const auto& [token, display] : mDisplays) {
-        beginFrame(display);
-        prepareFrame(display);
-        doDebugFlashRegions(display, repaintEverything);
-        doComposition(display, repaintEverything);
+    compositionengine::CompositionRefreshArgs refreshArgs;
+    refreshArgs.outputs.reserve(mDisplays.size());
+    for (const auto& [_, display] : mDisplays) {
+        refreshArgs.outputs.push_back(display->getCompositionDisplay());
+    }
+    mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
+        if (auto layerFE = layer->getCompositionEngineLayerFE())
+            refreshArgs.layers.push_back(layerFE);
+    });
+    refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+    for (sp<Layer> layer : mLayersWithQueuedFrames) {
+        if (auto layerFE = layer->getCompositionEngineLayerFE())
+            refreshArgs.layersWithQueuedFrames.push_back(layerFE);
     }
 
-    logLayerStats();
+    refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
+    refreshArgs.outputColorSetting = useColorManagement
+            ? mDisplayColorSetting
+            : compositionengine::OutputColorSetting::kUnmanaged;
+    refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace;
+    refreshArgs.forceOutputColorMode = mForceColorMode;
+
+    refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
+    refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty;
+
+    if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
+        refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix;
+        mDrawingState.colorMatrixChanged = false;
+    }
+
+    refreshArgs.devOptForceClientComposition = mDebugDisableHWC || mDebugRegion;
+
+    if (mDebugRegion != 0) {
+        refreshArgs.devOptFlashDirtyRegionsDelay =
+                std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0);
+    }
+
+    mGeometryInvalid = false;
+
+    // Store the present time just before calling to the composition engine so we could notify
+    // the scheduler.
+    const auto presentTime = systemTime();
+
+    mCompositionEngine->present(refreshArgs);
+    mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
+    // Reset the frame start time now that we've recorded this frame.
+    mFrameStartTime = 0;
+
+    mScheduler->onDisplayRefreshed(presentTime);
 
     postFrame();
     postComposition();
 
-    mHadClientComposition = false;
-    mHadDeviceComposition = false;
-    for (const auto& [token, displayDevice] : mDisplays) {
-        auto display = displayDevice->getCompositionDisplay();
-        const auto displayId = display->getId();
-        mHadClientComposition =
-                mHadClientComposition || getHwComposer().hasClientComposition(displayId);
-        mHadDeviceComposition =
-                mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);
-    }
+    mHadClientComposition =
+            std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
+                auto& displayDevice = tokenDisplayPair.second;
+                return displayDevice->getCompositionDisplay()->getState().usesClientComposition &&
+                        !displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
+            });
+    mHadDeviceComposition =
+            std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
+                auto& displayDevice = tokenDisplayPair.second;
+                return displayDevice->getCompositionDisplay()->getState().usesDeviceComposition;
+            });
+    mReusedClientComposition =
+            std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) {
+                auto& displayDevice = tokenDisplayPair.second;
+                return displayDevice->getCompositionDisplay()->getState().reusedClientComposition;
+            });
 
-    mVsyncModulator.onRefreshed(mHadClientComposition);
+    mVSyncModulator->onRefreshed(mHadClientComposition);
 
     mLayersWithQueuedFrames.clear();
     if (mVisibleRegionsDirty) {
         mVisibleRegionsDirty = false;
         if (mTracingEnabled) {
-            mTracing.notify(compositionTime, "visibleRegionsDirty");
+            mTracing.notify("visibleRegionsDirty");
         }
     }
+
+    if (mCompositionEngine->needsAnotherUpdate()) {
+        signalLayerUpdate();
+    }
 }
 
 
@@ -1878,174 +1975,6 @@
     return refreshNeeded;
 }
 
-void SurfaceFlinger::calculateWorkingSet() {
-    ATRACE_CALL();
-    ALOGV(__FUNCTION__);
-
-    // build the h/w work list
-    if (CC_UNLIKELY(mGeometryInvalid)) {
-        mGeometryInvalid = false;
-        for (const auto& [token, displayDevice] : mDisplays) {
-            auto display = displayDevice->getCompositionDisplay();
-
-            uint32_t zOrder = 0;
-
-            for (auto& layer : display->getOutputLayersOrderedByZ()) {
-                auto& compositionState = layer->editState();
-                compositionState.forceClientComposition = false;
-                if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) {
-                    compositionState.forceClientComposition = true;
-                }
-
-                // The output Z order is set here based on a simple counter.
-                compositionState.z = zOrder++;
-
-                // Update the display independent composition state. This goes
-                // to the general composition layer state structure.
-                // TODO: Do this once per compositionengine::CompositionLayer.
-                layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd,
-                                                          true);
-
-                // Recalculate the geometry state of the output layer.
-                layer->updateCompositionState(true);
-
-                // Write the updated geometry state to the HWC
-                layer->writeStateToHWC(true);
-            }
-        }
-    }
-
-    // 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);
-        }
-        Dataspace targetDataspace = Dataspace::UNKNOWN;
-        if (useColorManagement) {
-            ColorMode colorMode;
-            RenderIntent renderIntent;
-            pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent);
-            display->setColorMode(colorMode, targetDataspace, renderIntent);
-
-            if (isHdrColorMode(colorMode)) {
-                targetDataspace = Dataspace::UNKNOWN;
-            } else if (mColorSpaceAgnosticDataspace != Dataspace::UNKNOWN) {
-                targetDataspace = mColorSpaceAgnosticDataspace;
-            }
-        }
-
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            if (layer->isHdrY410()) {
-                layer->forceClientComposition(displayDevice);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) &&
-                       !profile->hasHDR10Support()) {
-                layer->forceClientComposition(displayDevice);
-            } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG ||
-                        layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) &&
-                       !profile->hasHLGSupport()) {
-                layer->forceClientComposition(displayDevice);
-            }
-
-            if (layer->getRoundedCornerState().radius > 0.0f) {
-                layer->forceClientComposition(displayDevice);
-            }
-
-            if (layer->getForceClientComposition(displayDevice)) {
-                ALOGV("[%s] Requesting Client composition", layer->getName().string());
-                layer->setCompositionType(displayDevice,
-                                          Hwc2::IComposerClient::Composition::CLIENT);
-                continue;
-            }
-
-            const auto& displayState = display->getState();
-            layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport,
-                                   displayDevice->getSupportedPerFrameMetadata(), targetDataspace);
-        }
-    }
-
-    mDrawingState.colorMatrixChanged = false;
-
-    for (const auto& [token, displayDevice] : mDisplays) {
-        auto display = displayDevice->getCompositionDisplay();
-        for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-            auto& layerState = layer->getCompositionLayer()->editState().frontEnd;
-            layerState.compositionType = static_cast<Hwc2::IComposerClient::Composition>(
-                    layer->getCompositionType(displayDevice));
-        }
-    }
-}
-
-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);
-    }
-
-    prepareFrame(displayDevice);
-}
-
-void SurfaceFlinger::logLayerStats() {
-    ATRACE_CALL();
-    if (CC_UNLIKELY(mLayerStats.isEnabled())) {
-        for (const auto& [token, display] : mDisplays) {
-            if (display->isPrimary()) {
-                mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(display));
-                return;
-            }
-        }
-
-        ALOGE("logLayerStats: no primary display");
-    }
-}
-
-void SurfaceFlinger::preComposition()
-{
-    ATRACE_CALL();
-    ALOGV("preComposition");
-
-    mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
-
-    bool needExtraInvalidate = false;
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        if (layer->onPreComposition(mRefreshStartTime)) {
-            needExtraInvalidate = true;
-        }
-    });
-
-    if (needExtraInvalidate) {
-        signalLayerUpdate();
-    }
-}
-
 void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime,
                                             std::shared_ptr<FenceTime>& presentFenceTime) {
     // Update queue of past composite+present times and determine the
@@ -2076,11 +2005,12 @@
                                                 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 = (mPhaseOffsets->getCurrentSfOffset() > 0)
-            ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
-            : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
+    nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+            ? (stats.vsyncPeriod -
+               (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
+            : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
 
-    // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
+    // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
     if (idealLatency <= 0) {
         idealLatency = stats.vsyncPeriod;
     }
@@ -2089,8 +2019,8 @@
     // 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 mPhaseOffsets->getCurrentSfOffset()
-    // with (presentLatency % interval).
+    // Snapping also allows an app to precisely calculate
+    // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
     nsecs_t bias = stats.vsyncPeriod / 2;
     int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
     nsecs_t snappedCompositeToPresentLatency =
@@ -2107,18 +2037,16 @@
     ATRACE_CALL();
     ALOGV("postComposition");
 
-    // Release any buffers which were replaced this frame
     nsecs_t dequeueReadyTime = systemTime();
     for (auto& layer : mLayersWithQueuedFrames) {
         layer->releasePendingBuffer(dequeueReadyTime);
     }
-
     // |mStateLock| not needed as we are on the main thread
     const auto displayDevice = getDefaultDisplayDeviceLocked();
 
     getBE().mGlCompositionDoneTimeline.updateSignalTimes();
     std::shared_ptr<FenceTime> glCompositionDoneFenceTime;
-    if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) {
+    if (displayDevice && displayDevice->getCompositionDisplay()->getState().usesClientComposition) {
         glCompositionDoneFenceTime =
                 std::make_shared<FenceTime>(displayDevice->getCompositionDisplay()
                                                     ->getRenderSurface()
@@ -2139,27 +2067,27 @@
     DisplayStatInfo stats;
     mScheduler->getDisplayStatInfo(&stats);
 
-    // 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(stats, mRefreshStartTime, presentFenceTime);
+    // We use the CompositionEngine::getLastFrameRefreshTimestamp() 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(stats, mCompositionEngine->getLastFrameRefreshTimestamp(),
+                           presentFenceTime);
     CompositorTiming compositorTiming;
     {
         std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock);
         compositorTiming = getBE().mCompositorTiming;
     }
 
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        bool frameLatched =
-                layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime,
-                                         presentFenceTime, compositorTiming);
+    mDrawingState.traverse([&](Layer* layer) {
+        bool frameLatched = layer->onPostComposition(displayDevice, glCompositionDoneFenceTime,
+                                                     presentFenceTime, compositorTiming);
         if (frameLatched) {
-            recordBufferingStats(layer->getName().string(),
-                    layer->getOccupancyHistory(false));
+            recordBufferingStats(layer->getName(), layer->getOccupancyHistory(false));
         }
     });
 
-    if (presentFenceTime->isValid()) {
+    if (displayDevice && displayDevice->isPrimary() &&
+        displayDevice->getPowerMode() == HWC_POWER_MODE_NORMAL && presentFenceTime->isValid()) {
         mScheduler->addPresentFence(presentFenceTime);
     }
 
@@ -2191,8 +2119,16 @@
         mTimeStats->incrementClientCompositionFrames();
     }
 
+    if (mReusedClientComposition) {
+        mTimeStats->incrementClientCompositionReusedFrames();
+    }
+
     mTimeStats->setPresentFenceGlobal(presentFenceTime);
 
+    const size_t sfConnections = mScheduler->getEventThreadConnectionCount(mSfConnectionHandle);
+    const size_t appConnections = mScheduler->getEventThreadConnectionCount(mAppConnectionHandle);
+    mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections);
+
     if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) &&
         !displayDevice->isPoweredOn()) {
         return;
@@ -2231,14 +2167,7 @@
     }
 
     mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]);
-
-    // Lock the mStateLock in case SurfaceFlinger is in the middle of applying a transaction.
-    // If we do not lock here, a callback could be sent without all of its SurfaceControls and
-    // metrics.
-    {
-        Mutex::Autolock _l(mStateLock);
-        mTransactionCompletedThread.sendCallbacks();
-    }
+    mTransactionCompletedThread.sendCallbacks();
 
     if (mLumaSampling && mRegionSamplingThread) {
         mRegionSamplingThread->notifyNewContent();
@@ -2247,6 +2176,7 @@
     // Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
     // side-effect of getTotalSize(), so we check that again here
     if (ATRACE_ENABLED()) {
+        // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
         ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
     }
 }
@@ -2261,263 +2191,12 @@
                 continue;
             }
 
-            layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform());
+            layer->computeBounds(displayDevice->getViewport().toFloatRect(), ui::Transform(),
+                                 0.f /* shadowRadius */);
         }
     }
 }
 
-void SurfaceFlinger::rebuildLayerStacks() {
-    ATRACE_CALL();
-    ALOGV("rebuildLayerStacks");
-
-    // rebuild the visible layer list per screen
-    if (CC_UNLIKELY(mVisibleRegionsDirty)) {
-        ATRACE_NAME("rebuildLayerStacks VR Dirty");
-        invalidateHwcGeometry();
-
-        for (const auto& pair : mDisplays) {
-            const auto& displayDevice = pair.second;
-            auto display = displayDevice->getCompositionDisplay();
-            const auto& displayState = display->getState();
-            Region opaqueRegion;
-            Region dirtyRegion;
-            compositionengine::Output::OutputLayers layersSortedByZ;
-            Vector<sp<Layer>> deprecated_layersSortedByZ;
-            Vector<sp<Layer>> layersNeedingFences;
-            const ui::Transform& tr = displayState.transform;
-            const Rect bounds = displayState.bounds;
-            if (displayState.isEnabled) {
-                computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion);
-
-                mDrawingState.traverseInZOrder([&](Layer* layer) {
-                    auto compositionLayer = layer->getCompositionLayer();
-                    if (compositionLayer == nullptr) {
-                        return;
-                    }
-
-                    const auto displayId = displayDevice->getId();
-                    sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE();
-                    LOG_ALWAYS_FATAL_IF(layerFE.get() == nullptr);
-
-                    bool needsOutputLayer = false;
-
-                    if (display->belongsInOutput(layer->getLayerStack(),
-                                                 layer->getPrimaryDisplayOnly())) {
-                        Region drawRegion(tr.transform(
-                                layer->visibleNonTransparentRegion));
-                        drawRegion.andSelf(bounds);
-                        if (!drawRegion.isEmpty()) {
-                            needsOutputLayer = true;
-                        }
-                    }
-
-                    if (needsOutputLayer) {
-                        layersSortedByZ.emplace_back(
-                                display->getOrCreateOutputLayer(displayId, compositionLayer,
-                                                                layerFE));
-                        deprecated_layersSortedByZ.add(layer);
-
-                        auto& outputLayerState = layersSortedByZ.back()->editState();
-                        outputLayerState.visibleRegion =
-                                tr.transform(layer->visibleRegion.intersect(displayState.viewport));
-                    } else if (displayId) {
-                        // For layers that are being removed from a HWC display,
-                        // and that have queued frames, add them to a a list of
-                        // released layers so we can properly set a fence.
-                        bool hasExistingOutputLayer =
-                                display->getOutputLayerForLayer(compositionLayer.get()) != nullptr;
-                        bool hasQueuedFrames = std::find(mLayersWithQueuedFrames.cbegin(),
-                                                         mLayersWithQueuedFrames.cend(),
-                                                         layer) != mLayersWithQueuedFrames.cend();
-
-                        if (hasExistingOutputLayer && hasQueuedFrames) {
-                            layersNeedingFences.add(layer);
-                        }
-                    }
-                });
-            }
-
-            display->setOutputLayersOrderedByZ(std::move(layersSortedByZ));
-
-            displayDevice->setVisibleLayersSortedByZ(deprecated_layersSortedByZ);
-            displayDevice->setLayersNeedingFences(layersNeedingFences);
-
-            Region undefinedRegion{bounds};
-            undefinedRegion.subtractSelf(tr.transform(opaqueRegion));
-
-            display->editState().undefinedRegion = undefinedRegion;
-            display->editState().dirtyRegion.orSelf(dirtyRegion);
-        }
-    }
-}
-
-// Returns a data space that fits all visible layers.  The returned data space
-// 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<DisplayDevice>& display,
-                                           Dataspace* outHdrDataSpace,
-                                           bool* outIsHdrClientComposition) const {
-    Dataspace bestDataSpace = Dataspace::V0_SRGB;
-    *outHdrDataSpace = Dataspace::UNKNOWN;
-
-    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;
-            case Dataspace::BT2020_PQ:
-            case Dataspace::BT2020_ITU_PQ:
-                bestDataSpace = Dataspace::DISPLAY_P3;
-                *outHdrDataSpace = Dataspace::BT2020_PQ;
-                *outIsHdrClientComposition = layer->getForceClientComposition(display);
-                break;
-            case Dataspace::BT2020_HLG:
-            case Dataspace::BT2020_ITU_HLG:
-                bestDataSpace = Dataspace::DISPLAY_P3;
-                // When there's mixed PQ content and HLG content, we set the HDR
-                // data space to be BT2020_PQ and convert HLG to PQ.
-                if (*outHdrDataSpace == Dataspace::UNKNOWN) {
-                    *outHdrDataSpace = Dataspace::BT2020_HLG;
-                }
-                break;
-            default:
-                break;
-        }
-    }
-
-    return bestDataSpace;
-}
-
-// Pick the ColorMode / Dataspace for the display device.
-void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& display, ColorMode* outMode,
-                                   Dataspace* outDataSpace, RenderIntent* outRenderIntent) const {
-    if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) {
-        *outMode = ColorMode::NATIVE;
-        *outDataSpace = Dataspace::UNKNOWN;
-        *outRenderIntent = RenderIntent::COLORIMETRIC;
-        return;
-    }
-
-    Dataspace hdrDataSpace;
-    bool isHdrClientComposition = false;
-    Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace, &isHdrClientComposition);
-
-    auto* profile = display->getCompositionDisplay()->getDisplayColorProfile();
-
-    switch (mForceColorMode) {
-        case ColorMode::SRGB:
-            bestDataSpace = Dataspace::V0_SRGB;
-            break;
-        case ColorMode::DISPLAY_P3:
-            bestDataSpace = Dataspace::DISPLAY_P3;
-            break;
-        default:
-            break;
-    }
-
-    // respect hdrDataSpace only when there is no legacy HDR support
-    const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN &&
-            !profile->hasLegacyHdrSupport(hdrDataSpace) && !isHdrClientComposition;
-    if (isHdr) {
-        bestDataSpace = hdrDataSpace;
-    }
-
-    RenderIntent intent;
-    switch (mDisplayColorSetting) {
-        case DisplayColorSetting::MANAGED:
-        case DisplayColorSetting::UNMANAGED:
-            intent = isHdr ? RenderIntent::TONE_MAP_COLORIMETRIC : RenderIntent::COLORIMETRIC;
-            break;
-        case DisplayColorSetting::ENHANCED:
-            intent = isHdr ? RenderIntent::TONE_MAP_ENHANCE : RenderIntent::ENHANCE;
-            break;
-        default: // vendor display color setting
-            intent = static_cast<RenderIntent>(mDisplayColorSetting);
-            break;
-    }
-
-    profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent);
-}
-
-void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& displayDevice) {
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-
-    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);
-
-    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]);
-
-    display->getRenderSurface()->beginFrame(mustRecompose);
-
-    if (mustRecompose) {
-        display->editState().lastCompositionHadVisibleLayers = !empty;
-    }
-}
-
-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();
-    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");
-
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-
-    if (displayState.isEnabled) {
-        // transform the dirty region into this screen's coordinate space
-        const Region dirtyRegion = display->getDirtyRegion(repaintEverything);
-
-        // repaint the framebuffer (if needed)
-        doDisplayComposition(displayDevice, dirtyRegion);
-
-        display->editState().dirtyRegion.clear();
-        display->getRenderSurface()->flip();
-    }
-    postFramebuffer(displayDevice);
-}
-
 void SurfaceFlinger::postFrame()
 {
     // |mStateLock| not needed as we are on the main thread
@@ -2530,65 +2209,6 @@
     }
 }
 
-void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) {
-    ATRACE_CALL();
-    ALOGV("postFramebuffer");
-
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-    const auto displayId = display->getId();
-
-    if (displayState.isEnabled) {
-        if (displayId) {
-            getHwComposer().presentAndGetReleaseFences(*displayId);
-        }
-        display->getRenderSurface()->onPresentDisplayCompleted();
-        for (auto& layer : display->getOutputLayersOrderedByZ()) {
-            sp<Fence> releaseFence = Fence::NO_FENCE;
-            bool usedClientComposition = true;
-
-            // 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.
-            if (layer->getState().hwc) {
-                const auto& hwcState = *layer->getState().hwc;
-                releaseFence =
-                        getHwComposer().getLayerReleaseFence(*displayId, hwcState.hwcLayer.get());
-                usedClientComposition =
-                        hwcState.hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT;
-            }
-
-            // If the layer was client composited in the previous frame, we
-            // need to merge with the previous client target acquire fence.
-            // 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 (usedClientComposition) {
-                releaseFence =
-                        Fence::merge("LayerRelease", releaseFence,
-                                     display->getRenderSurface()->getClientTargetAcquireFence());
-            }
-
-            layer->getLayerFE().onLayerDisplayed(releaseFence);
-        }
-
-        // We've got a list of layers needing fences, that are disjoint with
-        // display->getVisibleLayersSortedByZ.  The best we can do is to
-        // supply them with the present fence.
-        if (!displayDevice->getLayersNeedingFences().isEmpty()) {
-            sp<Fence> presentFence =
-                    displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE;
-            for (auto& layer : displayDevice->getLayersNeedingFences()) {
-                layer->getCompositionLayer()->getLayerFE()->onLayerDisplayed(presentFence);
-            }
-        }
-
-        if (displayId) {
-            getHwComposer().clearReleaseFences(*displayId);
-        }
-    }
-}
-
 void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
 {
     ATRACE_CALL();
@@ -2608,7 +2228,7 @@
     // with mStateLock held to guarantee that mCurrentState won't change
     // until the transaction is committed.
 
-    mVsyncModulator.onTransactionHandled();
+    mVSyncModulator->onTransactionHandled();
     transactionFlags = getTransactionFlags(eTransactionMask);
     handleTransactionLocked(transactionFlags);
 
@@ -2626,27 +2246,38 @@
             continue;
         }
 
+        const DisplayId displayId = info->id;
+        const auto it = mPhysicalDisplayTokens.find(displayId);
+
         if (event.connection == HWC2::Connection::Connected) {
-            if (!mPhysicalDisplayTokens.count(info->id)) {
-                ALOGV("Creating display %s", to_string(info->id).c_str());
-                mPhysicalDisplayTokens[info->id] = new BBinder();
+            if (it == mPhysicalDisplayTokens.end()) {
+                ALOGV("Creating display %s", to_string(displayId).c_str());
+
+                if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) {
+                    initScheduler(displayId);
+                }
+
                 DisplayDeviceState state;
-                state.displayId = info->id;
+                state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId)};
                 state.isSecure = true; // All physical displays are currently considered secure.
                 state.displayName = info->name;
-                mCurrentState.displays.add(mPhysicalDisplayTokens[info->id], state);
+
+                sp<IBinder> token = new BBinder();
+                mCurrentState.displays.add(token, state);
+                mPhysicalDisplayTokens.emplace(displayId, std::move(token));
+
                 mInterceptor->saveDisplayCreation(state);
             }
         } else {
-            ALOGV("Removing display %s", to_string(info->id).c_str());
+            ALOGV("Removing display %s", to_string(displayId).c_str());
 
-            ssize_t index = mCurrentState.displays.indexOfKey(mPhysicalDisplayTokens[info->id]);
+            const ssize_t index = mCurrentState.displays.indexOfKey(it->second);
             if (index >= 0) {
                 const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
                 mInterceptor->saveDisplayDeletion(state.sequenceId);
                 mCurrentState.displays.removeItemsAt(index);
             }
-            mPhysicalDisplayTokens.erase(info->id);
+            mPhysicalDisplayTokens.erase(it);
         }
 
         processDisplayChangesLocked();
@@ -2656,8 +2287,8 @@
 }
 
 void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    mScheduler->hotplugReceived(mAppConnectionHandle, displayId, connected);
-    mScheduler->hotplugReceived(mSfConnectionHandle, displayId, connected);
+    mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected);
+    mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected);
 }
 
 sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
@@ -2666,11 +2297,15 @@
         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;
+    creationArgs.powerAdvisor = displayId ? &mPowerAdvisor : nullptr;
+
+    if (const auto& physical = state.physical) {
+        creationArgs.connectionType = physical->type;
+    }
 
     const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked();
     creationArgs.isPrimary = isInternalDisplay;
@@ -2705,8 +2340,8 @@
         nativeWindow->setSwapInterval(nativeWindow.get(), 0);
     }
 
-    creationArgs.displayInstallOrientation =
-            isInternalDisplay ? primaryDisplayOrientation : DisplayState::eOrientationDefault;
+    creationArgs.physicalOrientation =
+            isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0;
 
     // virtual displays are always considered enabled
     creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
@@ -2723,11 +2358,14 @@
         defaultColorMode = ColorMode::SRGB;
         defaultDataSpace = Dataspace::V0_SRGB;
     }
-    display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace,
-                                                   RenderIntent::COLORIMETRIC);
+    display->getCompositionDisplay()->setColorProfile(
+            compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace,
+                                                    RenderIntent::COLORIMETRIC,
+                                                    Dataspace::UNKNOWN});
     if (!state.isVirtual()) {
         LOG_ALWAYS_FATAL_IF(!displayId);
-        display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
+        auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
+        display->setActiveConfig(activeConfigId);
     }
 
     display->setLayerStack(state.layerStack);
@@ -2799,6 +2437,9 @@
                     }
                     if (state.width != draw[i].width || state.height != draw[i].height) {
                         display->setDisplaySize(state.width, state.height);
+                        if (display->isPrimary()) {
+                            mScheduler->onPrimaryDisplayAreaChanged(state.width * state.height);
+                        }
                     }
                 }
             }
@@ -2856,8 +2497,8 @@
                              "surface is provided (%p), ignoring it",
                              state.surface.get());
 
-                    displayId = state.displayId;
-                    LOG_ALWAYS_FATAL_IF(!displayId);
+                    LOG_FATAL_IF(!state.physical);
+                    displayId = state.physical->id;
                     dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer);
                     producer = bqProducer;
                 }
@@ -2871,6 +2512,12 @@
                         LOG_ALWAYS_FATAL_IF(!displayId);
                         dispatchDisplayHotplugEvent(displayId->value, true);
                     }
+
+                    const auto displayDevice = mDisplays[displayToken];
+                    if (displayDevice->isPrimary()) {
+                        mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
+                                                                displayDevice->getHeight());
+                    }
                 }
             }
         }
@@ -2881,9 +2528,11 @@
 
 void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
 {
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+
     // Notify all layers of available frames
-    mCurrentState.traverseInZOrder([](Layer* layer) {
-        layer->notifyAvailableFrames();
+    mCurrentState.traverse([expectedPresentTime](Layer* layer) {
+        layer->notifyAvailableFrames(expectedPresentTime);
     });
 
     /*
@@ -2892,7 +2541,7 @@
      */
 
     if ((transactionFlags & eTraversalNeeded) || mTraversalNeededMainThread) {
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
             if (!trFlags) return;
 
@@ -2928,7 +2577,7 @@
         // display is used to calculate the hint, otherwise we use the
         // default display.
         //
-        // NOTE: we do this here, rather than in rebuildLayerStacks() so that
+        // NOTE: we do this here, rather than when presenting the display so that
         // the hint is set before we acquire a buffer from the surface texture.
         //
         // NOTE: layer transactions have taken place already, so we use their
@@ -2939,7 +2588,7 @@
         sp<const DisplayDevice> hintDisplay;
         uint32_t currentlayerStack = 0;
         bool first = true;
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) {
             // NOTE: we rely on the fact that layers are sorted by
             // layerStack first (so we don't have to traverse the list
             // of displays for every layer).
@@ -3055,36 +2704,97 @@
 
 void SurfaceFlinger::updateCursorAsync()
 {
-    for (const auto& [token, display] : mDisplays) {
-        if (!display->getId()) {
-            continue;
-        }
-
-        for (auto& layer : display->getVisibleLayersSortedByZ()) {
-            layer->updateCursorPosition(display);
+    compositionengine::CompositionRefreshArgs refreshArgs;
+    for (const auto& [_, display] : mDisplays) {
+        if (display->getId()) {
+            refreshArgs.outputs.push_back(display->getCompositionDisplay());
         }
     }
+
+    mCompositionEngine->updateCursorAsync(refreshArgs);
 }
 
-void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) {
-    if (layer->hasReadyFrame()) {
-        bool ignored = false;
-        layer->latchBuffer(ignored, systemTime());
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
+                                       Scheduler::ConfigEvent event) NO_THREAD_SAFETY_ANALYSIS {
+    // If this is called from the main thread mStateLock must be locked before
+    // Currently the only way to call this function from the main thread is from
+    // Sheduler::chooseRefreshRateForContent
+
+    ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
+    changeRefreshRateLocked(refreshRate, event);
+}
+
+void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
+    if (mScheduler) {
+        // In practice it's not allowed to hotplug in/out the primary display once it's been
+        // connected during startup, but some tests do it, so just warn and return.
+        ALOGW("Can't re-init scheduler");
+        return;
     }
-    layer->releasePendingBuffer(systemTime());
+
+    auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
+    mRefreshRateConfigs =
+            std::make_unique<scheduler::RefreshRateConfigs>(getHwComposer().getConfigs(
+                                                                    primaryDisplayId),
+                                                            currentConfig);
+    mRefreshRateStats =
+            std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats,
+                                                          currentConfig, HWC_POWER_MODE_OFF);
+    mRefreshRateStats->setConfigMode(currentConfig);
+
+    mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+
+    // start the EventThread
+    mScheduler =
+            getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
+                                         *mRefreshRateConfigs, *this);
+    mAppConnectionHandle =
+            mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
+                                         impl::EventThread::InterceptVSyncsCallback());
+    mSfConnectionHandle =
+            mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
+                                         [this](nsecs_t timestamp) {
+                                             mInterceptor->saveVSyncEvent(timestamp);
+                                         });
+
+    mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
+    mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
+                            mPhaseConfiguration->getCurrentOffsets());
+
+    mRegionSamplingThread =
+            new RegionSamplingThread(*this, *mScheduler,
+                                     RegionSamplingThread::EnvironmentTimingTunables());
+    // Dispatch a config change request for the primary display on scheduler
+    // initialization, so that the EventThreads always contain a reference to a
+    // prior configuration.
+    //
+    // This is a bit hacky, but this avoids a back-pointer into the main SF
+    // classes from EventThread, and there should be no run-time binder cost
+    // anyway since there are no connected apps at this point.
+    const nsecs_t vsyncPeriod =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).vsyncPeriod;
+    mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig,
+                                vsyncPeriod);
 }
 
 void SurfaceFlinger::commitTransaction()
 {
+    withTracingLock([this]() { commitTransactionLocked(); });
+
+    mTransactionPending = false;
+    mAnimTransactionPending = false;
+    mTransactionCV.broadcast();
+}
+
+void SurfaceFlinger::commitTransactionLocked() {
     if (!mLayersPendingRemoval.isEmpty()) {
         // Notify removed layers now that they can't be drawn from
         for (const auto& l : mLayersPendingRemoval) {
-            recordBufferingStats(l->getName().string(),
-                    l->getOccupancyHistory(true));
+            recordBufferingStats(l->getName(), l->getOccupancyHistory(true));
 
             // Ensure any buffers set to display on any children are released.
             if (l->isRemovedFromCurrentState()) {
-                latchAndReleaseBuffer(l);
+                l->latchAndReleaseBuffer();
             }
 
             // If the layer has been removed and has no parent, then it will not be reachable
@@ -3101,27 +2811,22 @@
     // we composite should be considered an animation as well.
     mAnimCompositionPending = mAnimTransactionPending;
 
-    withTracingLock([&]() {
-        mDrawingState = mCurrentState;
-        // clear the "changed" flags in current state
-        mCurrentState.colorMatrixChanged = false;
+    mDrawingState = mCurrentState;
+    // clear the "changed" flags in current state
+    mCurrentState.colorMatrixChanged = false;
 
-        mDrawingState.traverseInZOrder([&](Layer* layer) {
-            layer->commitChildList();
+    mDrawingState.traverse([&](Layer* layer) {
+        layer->commitChildList();
 
-            // If the layer can be reached when traversing mDrawingState, then the layer is no
-            // longer offscreen. Remove the layer from the offscreenLayer set.
-            if (mOffscreenLayers.count(layer)) {
-                mOffscreenLayers.erase(layer);
-            }
-        });
-
-        commitOffscreenLayers();
+        // If the layer can be reached when traversing mDrawingState, then the layer is no
+        // longer offscreen. Remove the layer from the offscreenLayer set.
+        if (mOffscreenLayers.count(layer)) {
+            mOffscreenLayers.erase(layer);
+        }
     });
 
-    mTransactionPending = false;
-    mAnimTransactionPending = false;
-    mTransactionCV.broadcast();
+    commitOffscreenLayers();
+    mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); });
 }
 
 void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
@@ -3146,7 +2851,7 @@
 
 void SurfaceFlinger::commitOffscreenLayers() {
     for (Layer* offscreenLayer : mOffscreenLayers) {
-        offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [](Layer* layer) {
+        offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) {
             uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
             if (!trFlags) return;
 
@@ -3156,147 +2861,6 @@
     }
 }
 
-void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
-                                           Region& outDirtyRegion, Region& outOpaqueRegion) {
-    ATRACE_CALL();
-    ALOGV("computeVisibleRegions");
-
-    auto display = displayDevice->getCompositionDisplay();
-
-    Region aboveOpaqueLayers;
-    Region aboveCoveredLayers;
-    Region dirty;
-
-    outDirtyRegion.clear();
-
-    mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
-        // start with the whole surface at its current location
-        const Layer::State& s(layer->getDrawingState());
-
-        // only consider the layers on the given layer stack
-        if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) {
-            return;
-        }
-
-        /*
-         * opaqueRegion: area of a surface that is fully opaque.
-         */
-        Region opaqueRegion;
-
-        /*
-         * visibleRegion: area of a surface that is visible on screen
-         * and not fully transparent. This is essentially the layer's
-         * footprint minus the opaque regions above it.
-         * Areas covered by a translucent surface are considered visible.
-         */
-        Region visibleRegion;
-
-        /*
-         * coveredRegion: area of a surface that is covered by all
-         * visible regions above it (which includes the translucent areas).
-         */
-        Region coveredRegion;
-
-        /*
-         * transparentRegion: area of a surface that is hinted to be completely
-         * transparent. This is only used to tell when the layer has no visible
-         * non-transparent regions and can be removed from the layer list. It
-         * does not affect the visibleRegion of this layer or any layers
-         * beneath it. The hint may not be correct if apps don't respect the
-         * SurfaceView restrictions (which, sadly, some don't).
-         */
-        Region transparentRegion;
-
-
-        // handle hidden surfaces by setting the visible region to empty
-        if (CC_LIKELY(layer->isVisible())) {
-            const bool translucent = !layer->isOpaque(s);
-            Rect bounds(layer->getScreenBounds());
-
-            visibleRegion.set(bounds);
-            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(layer->getActiveTransparentRegion(s));
-                    } else {
-                        // transformation too complex, can't do the
-                        // transparent region optimization.
-                        transparentRegion.clear();
-                    }
-                }
-
-                // compute the opaque region
-                const int32_t layerOrientation = tr.getOrientation();
-                if (layer->getAlpha() == 1.0f && !translucent &&
-                        layer->getRoundedCornerState().radius == 0.0f &&
-                        ((layerOrientation & ui::Transform::ROT_INVALID) == false)) {
-                    // the opaque region is the layer's footprint
-                    opaqueRegion = visibleRegion;
-                }
-            }
-        }
-
-        if (visibleRegion.isEmpty()) {
-            layer->clearVisibilityRegions();
-            return;
-        }
-
-        // Clip the covered region to the visible region
-        coveredRegion = aboveCoveredLayers.intersect(visibleRegion);
-
-        // Update aboveCoveredLayers for next (lower) layer
-        aboveCoveredLayers.orSelf(visibleRegion);
-
-        // subtract the opaque region covered by the layers above us
-        visibleRegion.subtractSelf(aboveOpaqueLayers);
-
-        // compute this layer's dirty region
-        if (layer->contentDirty) {
-            // we need to invalidate the whole region
-            dirty = visibleRegion;
-            // as well, as the old visible region
-            dirty.orSelf(layer->visibleRegion);
-            layer->contentDirty = false;
-        } else {
-            /* compute the exposed region:
-             *   the exposed region consists of two components:
-             *   1) what's VISIBLE now and was COVERED before
-             *   2) what's EXPOSED now less what was EXPOSED before
-             *
-             * note that (1) is conservative, we start with the whole
-             * visible region but only keep what used to be covered by
-             * something -- which mean it may have been exposed.
-             *
-             * (2) handles areas that were not covered by anything but got
-             * exposed because of a resize.
-             */
-            const Region newExposed = visibleRegion - coveredRegion;
-            const Region oldVisibleRegion = layer->visibleRegion;
-            const Region oldCoveredRegion = layer->coveredRegion;
-            const Region oldExposed = oldVisibleRegion - oldCoveredRegion;
-            dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed);
-        }
-        dirty.subtractSelf(aboveOpaqueLayers);
-
-        // accumulate to the screen dirty region
-        outDirtyRegion.orSelf(dirty);
-
-        // Update aboveOpaqueLayers for next (lower) layer
-        aboveOpaqueLayers.orSelf(opaqueRegion);
-
-        // Store the visible region in screen space
-        layer->setVisibleRegion(visibleRegion);
-        layer->setCoveredRegion(coveredRegion);
-        layer->setVisibleNonTransparentRegion(
-                visibleRegion.subtract(transparentRegion));
-    });
-
-    outOpaqueRegion = aboveOpaqueLayers;
-}
-
 void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) {
     for (const auto& [token, displayDevice] : mDisplays) {
         auto display = displayDevice->getCompositionDisplay();
@@ -3317,6 +2881,8 @@
     bool frameQueued = false;
     bool newDataLatched = false;
 
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
+
     // Store the set of layers that need updates. This set must not change as
     // buffers are being latched, as this could result in a deadlock.
     // Example: Two producers share the same command stream and:
@@ -3326,10 +2892,9 @@
     // 3.) Layer 1 is latched.
     // 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) {
+    mDrawingState.traverse([&](Layer* layer) {
         if (layer->hasReadyFrame()) {
             frameQueued = true;
-            const nsecs_t expectedPresentTime = getExpectedPresentTime();
             if (layer->shouldPresentNow(expectedPresentTime)) {
                 mLayersWithQueuedFrames.push_back(layer);
             } else {
@@ -3341,13 +2906,21 @@
         }
     });
 
+    // The client can continue submitting buffers for offscreen layers, but they will not
+    // be shown on screen. Therefore, we need to latch and release buffers of offscreen
+    // layers to ensure dequeueBuffer doesn't block indefinitely.
+    for (Layer* offscreenLayer : mOffscreenLayers) {
+        offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+                                         [&](Layer* l) { l->latchAndReleaseBuffer(); });
+    }
+
     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) {
-            if (layer->latchBuffer(visibleRegions, latchTime)) {
+            if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
                 mLayersPendingRefresh.push_back(layer);
             }
             layer->useSurfaceDamage();
@@ -3372,6 +2945,8 @@
         mBootStage = BootStage::BOOTANIMATION;
     }
 
+    mDrawingState.traverse([&](Layer* layer) { layer->updateCloneBufferInfo(); });
+
     // Only continue with the refresh if there is actually new work to do
     return !mLayersWithQueuedFrames.empty() && newDataLatched;
 }
@@ -3381,207 +2956,6 @@
     mGeometryInvalid = true;
 }
 
-void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice,
-                                          const Region& inDirtyRegion) {
-    auto display = displayDevice->getCompositionDisplay();
-    // 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)
-    if (!displayDevice->getId() && inDirtyRegion.isEmpty()) {
-        ALOGV("Skipping display composition");
-        return;
-    }
-
-    ALOGV("doDisplayComposition");
-    base::unique_fd readyFence;
-    if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return;
-
-    // swap buffers (presentation)
-    display->getRenderSurface()->queueBuffer(std::move(readyFence));
-}
-
-bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice,
-                                       const Region& debugRegion, base::unique_fd* readyFence) {
-    ATRACE_CALL();
-    ALOGV("doComposeSurfaces");
-
-    auto display = displayDevice->getCompositionDisplay();
-    const auto& displayState = display->getState();
-    const auto displayId = display->getId();
-    auto& renderEngine = getRenderEngine();
-    const bool supportProtectedContent = renderEngine.supportsProtectedContent();
-
-    const Region bounds(displayState.bounds);
-    const DisplayRenderArea renderArea(displayDevice);
-    const bool hasClientComposition = getHwComposer().hasClientComposition(displayId);
-    ATRACE_INT("hasClientComposition", hasClientComposition);
-
-    bool applyColorMatrix = false;
-
-    renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<renderengine::LayerSettings> clientCompositionLayers;
-    sp<GraphicBuffer> buf;
-    base::unique_fd fd;
-
-    if (hasClientComposition) {
-        ALOGV("hasClientComposition");
-
-        if (displayDevice->isPrimary() && supportProtectedContent) {
-            bool needsProtected = false;
-            for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-                // If the layer is a protected layer, mark protected context is needed.
-                if (layer->isProtected()) {
-                    needsProtected = true;
-                    break;
-                }
-            }
-            if (needsProtected != renderEngine.isProtected()) {
-                renderEngine.useProtectedContext(needsProtected);
-            }
-            if (needsProtected != display->getRenderSurface()->isProtected() &&
-                needsProtected == renderEngine.isProtected()) {
-                display->getRenderSurface()->setProtected(needsProtected);
-            }
-        }
-
-        buf = display->getRenderSurface()->dequeueBuffer(&fd);
-
-        if (buf == nullptr) {
-            ALOGW("Dequeuing buffer for display [%s] failed, bailing out of "
-                  "client composition for this frame",
-                  displayDevice->getDisplayName().c_str());
-            return false;
-        }
-
-        clientCompositionDisplay.physicalDisplay = displayState.scissor;
-        clientCompositionDisplay.clip = displayState.scissor;
-        const ui::Transform& displayTransform = displayState.transform;
-        clientCompositionDisplay.globalTransform = displayTransform.asMatrix4();
-        clientCompositionDisplay.orientation = displayState.orientation;
-
-        const auto* profile = display->getDisplayColorProfile();
-        Dataspace outputDataspace = Dataspace::UNKNOWN;
-        if (profile->hasWideColorGamut()) {
-            outputDataspace = displayState.dataspace;
-        }
-        clientCompositionDisplay.outputDataspace = outputDataspace;
-        clientCompositionDisplay.maxLuminance =
-                profile->getHdrCapabilities().getDesiredMaxLuminance();
-
-        const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId);
-        const bool skipClientColorTransform =
-                getHwComposer()
-                        .hasDisplayCapability(displayId,
-                                              HWC2::DisplayCapability::SkipClientColorTransform);
-
-        // Compute the global color transform matrix.
-        applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform;
-        if (applyColorMatrix) {
-            clientCompositionDisplay.colorTransform = displayState.colorTransformMat;
-        }
-    }
-
-    /*
-     * and then, render the layers targeted at the framebuffer
-     */
-
-    ALOGV("Rendering client layers");
-    bool firstLayer = true;
-    Region clearRegion = Region::INVALID_REGION;
-    for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
-        const Region viewportRegion(displayState.viewport);
-        const Region clip(viewportRegion.intersect(layer->visibleRegion));
-        ALOGV("Layer: %s", layer->getName().string());
-        ALOGV("  Composition type: %s", toString(layer->getCompositionType(displayDevice)).c_str());
-        if (!clip.isEmpty()) {
-            switch (layer->getCompositionType(displayDevice)) {
-                case Hwc2::IComposerClient::Composition::CURSOR:
-                case Hwc2::IComposerClient::Composition::DEVICE:
-                case Hwc2::IComposerClient::Composition::SIDEBAND:
-                case Hwc2::IComposerClient::Composition::SOLID_COLOR: {
-                    LOG_ALWAYS_FATAL_IF(!displayId);
-                    const Layer::State& state(layer->getDrawingState());
-                    if (layer->getClearClientTarget(displayDevice) && !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
-                        renderengine::LayerSettings layerSettings;
-                        Region dummyRegion;
-                        bool prepared =
-                                layer->prepareClientLayer(renderArea, clip, dummyRegion,
-                                                          supportProtectedContent, 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::IComposerClient::Composition::CLIENT: {
-                    renderengine::LayerSettings layerSettings;
-                    bool prepared =
-                            layer->prepareClientLayer(renderArea, clip, clearRegion,
-                                                      supportProtectedContent, layerSettings);
-                    if (prepared) {
-                        clientCompositionLayers.push_back(layerSettings);
-                    }
-                    break;
-                }
-                default:
-                    break;
-            }
-        } else {
-            ALOGV("  Skipping for empty clip");
-        }
-        firstLayer = false;
-    }
-
-    // Perform some cleanup steps if we used client composition.
-    if (hasClientComposition) {
-        clientCompositionDisplay.clearRegion = clearRegion;
-
-        // We boost GPU frequency here because there will be color spaces conversion
-        // and it's expensive. We boost the GPU frequency so that GPU composition can
-        // finish in time. We must reset GPU frequency afterwards, because high frequency
-        // consumes extra battery.
-        const bool expensiveRenderingExpected =
-                clientCompositionDisplay.outputDataspace == Dataspace::DISPLAY_P3;
-        if (expensiveRenderingExpected && displayId) {
-            mPowerAdvisor.setExpensiveRenderingExpected(*displayId, true);
-        }
-        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);
-            }
-        }
-        renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
-                                buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
-                                readyFence);
-    } else if (displayId) {
-        mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false);
-    }
-    return true;
-}
-
-void SurfaceFlinger::drawWormhole(const Region& region) const {
-    auto& engine(getRenderEngine());
-    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<IBinder>& parentHandle,
@@ -3600,7 +2974,7 @@
         }
 
         if (mNumLayers >= MAX_LAYERS) {
-            ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers,
+            ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
                   MAX_LAYERS);
             return NO_MEMORY;
         }
@@ -3624,7 +2998,7 @@
                                         mMaxGraphicBufferProducerListSize,
                                 "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
                                 mGraphicBufferProducerList.size(),
-                                mMaxGraphicBufferProducerListSize, mNumLayers);
+                                mMaxGraphicBufferProducerListSize, mNumLayers.load());
         }
         mLayersAdded = true;
     }
@@ -3650,7 +3024,7 @@
 uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags,
                                              Scheduler::TransactionStart transactionStart) {
     uint32_t old = mTransactionFlags.fetch_or(flags);
-    mVsyncModulator.setTransactionStart(transactionStart);
+    mVSyncModulator->setTransactionStart(transactionStart);
     if ((old & flags)==0) { // wake the server up
         signalTransaction();
     }
@@ -3673,6 +3047,7 @@
             while (!transactionQueue.empty()) {
                 const auto& transaction = transactionQueue.front();
                 if (!transactionIsReadyToBeApplied(transaction.desiredPresentTime,
+                                                   true /* useCachedExpectedPresentTime */,
                                                    transaction.states)) {
                     setTransactionFlags(eTransactionFlushNeeded);
                     break;
@@ -3680,9 +3055,9 @@
                 transactions.push_back(transaction);
                 applyTransactionState(transaction.states, transaction.displays, transaction.flags,
                                       mPendingInputWindowCommands, transaction.desiredPresentTime,
-                                      transaction.buffer, transaction.callback,
-                                      transaction.postTime, transaction.privileged,
-                                      /*isMainThread*/ true);
+                                      transaction.buffer, transaction.postTime,
+                                      transaction.privileged, transaction.hasListenerCallbacks,
+                                      transaction.listenerCallbacks, /*isMainThread*/ true);
                 transactionQueue.pop();
                 flushedATransaction = true;
             }
@@ -3702,31 +3077,14 @@
     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
-        // one of our own. A malicious client could give us a nullptr
-        // IInterface, or one of its own or even one of our own but a
-        // different type. All these situations would cause us to crash.
-        if (state.client == nullptr) {
-            return true;
-        }
-
-        sp<IBinder> binder = IInterface::asBinder(state.client);
-        if (binder == nullptr) {
-            return true;
-        }
-
-        if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) == nullptr) {
-            return true;
-        }
-    }
-    return false;
-}
 
 bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                                   bool useCachedExpectedPresentTime,
                                                    const Vector<ComposerState>& states) {
-    nsecs_t expectedPresentTime = getExpectedPresentTime();
+    if (!useCachedExpectedPresentTime)
+        populateExpectedPresentTime();
+
+    const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
     // 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 &&
@@ -3746,13 +3104,11 @@
     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,
-                                         const client_cache_t& uncacheBuffer,
-                                         const std::vector<ListenerCallbacks>& listenerCallbacks) {
+void SurfaceFlinger::setTransactionState(
+        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands,
+        int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
+        const std::vector<ListenerCallbacks>& listenerCallbacks) {
     ATRACE_CALL();
 
     const int64_t postTime = systemTime();
@@ -3761,10 +3117,6 @@
 
     Mutex::Autolock _l(mStateLock);
 
-    if (containsAnyInvalidClientState(states)) {
-        return;
-    }
-
     // If its TransactionQueue already has a pending TransactionState or if it is pending
     auto itr = mTransactionQueues.find(applyToken);
     // if this is an animation frame, wait until prior animation frame has
@@ -3781,27 +3133,28 @@
             itr = mTransactionQueues.find(applyToken);
         }
     }
-    if (itr != mTransactionQueues.end() ||
-        !transactionIsReadyToBeApplied(desiredPresentTime, states)) {
+
+    // Expected present time is computed and cached on invalidate, so it may be stale.
+    if (itr != mTransactionQueues.end() || !transactionIsReadyToBeApplied(
+            desiredPresentTime, false /* useCachedExpectedPresentTime */, states)) {
         mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime,
-                                               uncacheBuffer, listenerCallbacks, postTime,
-                                               privileged);
+                                               uncacheBuffer, postTime, privileged,
+                                               hasListenerCallbacks, listenerCallbacks);
         setTransactionFlags(eTransactionFlushNeeded);
         return;
     }
 
     applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime,
-                          uncacheBuffer, listenerCallbacks, postTime, privileged);
+                          uncacheBuffer, postTime, privileged, hasListenerCallbacks,
+                          listenerCallbacks);
 }
 
-void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states,
-                                           const Vector<DisplayState>& displays, uint32_t flags,
-                                           const InputWindowCommands& inputWindowCommands,
-                                           const int64_t desiredPresentTime,
-                                           const client_cache_t& uncacheBuffer,
-                                           const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                           const int64_t postTime, bool privileged,
-                                           bool isMainThread) {
+void SurfaceFlinger::applyTransactionState(
+        const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags,
+        const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
+        const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged,
+        bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+        bool isMainThread) {
     uint32_t transactionFlags = 0;
 
     if (flags & eAnimation) {
@@ -3824,24 +3177,27 @@
         transactionFlags |= setDisplayStateLocked(display);
     }
 
-    // In case the client has sent a Transaction that should receive callbacks but without any
-    // SurfaceControls that should be included in the callback, send the listener and callbackIds
-    // to the callback thread so it can send an empty callback
-    if (!listenerCallbacks.empty()) {
-        mTransactionCompletedThread.run();
-    }
-    for (const auto& [listener, callbackIds] : listenerCallbacks) {
-        mTransactionCompletedThread.addCallback(listener, callbackIds);
+    // start and end registration for listeners w/ no surface so they can get their callback.  Note
+    // that listeners with SurfaceControls will start registration during setClientStateLocked
+    // below.
+    for (const auto& listener : listenerCallbacks) {
+        mTransactionCompletedThread.startRegistration(listener);
+        mTransactionCompletedThread.endRegistration(listener);
     }
 
+    std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces;
     uint32_t clientStateFlags = 0;
     for (const ComposerState& state : states) {
-        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks,
-                                                 postTime, privileged);
+        clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged,
+                                                 listenerCallbacksWithSurfaces);
+    }
+
+    for (const auto& listenerCallback : listenerCallbacksWithSurfaces) {
+        mTransactionCompletedThread.endRegistration(listenerCallback);
     }
 
     // If the state doesn't require a traversal and there are callbacks, send them now
-    if (!(clientStateFlags & eTraversalNeeded) && !listenerCallbacks.empty()) {
+    if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) {
         mTransactionCompletedThread.sendCallbacks();
     }
     transactionFlags |= clientStateFlags;
@@ -3969,22 +3325,36 @@
 }
 
 uint32_t SurfaceFlinger::setClientStateLocked(
-        const ComposerState& composerState, int64_t desiredPresentTime,
-        const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
-        bool privileged) {
+        const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+        bool privileged,
+        std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) {
     const layer_state_t& s = composerState.state;
-    sp<Client> client(static_cast<Client*>(composerState.client.get()));
 
-    sp<Layer> layer(client->getLayerUser(s.surface));
+    for (auto& listener : s.listeners) {
+        // note that startRegistration will not re-register if the listener has
+        // already be registered for a prior surface control
+        mTransactionCompletedThread.startRegistration(listener);
+        listenerCallbacks.insert(listener);
+    }
+
+    sp<Layer> layer = nullptr;
+    if (s.surface) {
+        layer = fromHandle(s.surface);
+    } else {
+        // The client may provide us a null handle. Treat it as if the layer was removed.
+        ALOGW("Attempt to set client state with a null layer handle");
+    }
     if (layer == nullptr) {
+        for (auto& [listener, callbackIds] : s.listeners) {
+            mTransactionCompletedThread.registerUnpresentedCallbackHandle(
+                    new CallbackHandle(listener, callbackIds, s.surface));
+        }
         return 0;
     }
 
     uint32_t flags = 0;
 
     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.
@@ -3992,8 +3362,13 @@
         layer->pushPendingState();
     }
 
+    // Only set by BLAST adapter layers
+    if (what & layer_state_t::eProducerDisconnect) {
+        layer->onDisconnect();
+    }
+
     if (what & layer_state_t::ePositionChanged) {
-        if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) {
+        if (layer->setPosition(s.x, s.y)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4084,24 +3459,27 @@
             flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eCropChanged_legacy) {
-        if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize))
-            flags |= eTraversalNeeded;
+        if (layer->setCrop_legacy(s.crop_legacy)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eCornerRadiusChanged) {
         if (layer->setCornerRadius(s.cornerRadius))
             flags |= eTraversalNeeded;
     }
+    if (what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
+    }
     if (what & layer_state_t::eLayerStackChanged) {
         ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
         // We only allow setting layer stacks for top level layers,
         // everything else inherits layer stack from its parent.
         if (layer->hasParent()) {
             ALOGE("Attempt to set layer stack on layer with parent (%s) is invalid",
-                    layer->getName().string());
+                  layer->getDebugName());
         } else if (idx < 0) {
             ALOGE("Attempt to set layer stack on layer without parent (%s) that "
-                    "that also does not appear in the top level layer list. Something"
-                    " has gone wrong.", layer->getName().string());
+                  "that also does not appear in the top level layer list. Something"
+                  " has gone wrong.",
+                  layer->getDebugName());
         } else if (layer->setLayerStack(s.layerStack)) {
             mCurrentState.layersSortedByZ.removeAt(idx);
             mCurrentState.layersSortedByZ.add(layer);
@@ -4127,15 +3505,6 @@
         // 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::eReparent) {
-        bool hadParent = layer->hasParent();
-        if (layer->reparent(s.parentHandleForChild)) {
-            if (!hadParent) {
-                mCurrentState.layersSortedByZ.remove(layer);
-            }
-            flags |= eTransactionNeeded|eTraversalNeeded;
-        }
-    }
     if (what & layer_state_t::eReparentChildren) {
         if (layer->reparentChildren(s.reparentHandle)) {
             flags |= eTransactionNeeded|eTraversalNeeded;
@@ -4196,9 +3565,35 @@
             flags |= eTraversalNeeded;
         }
     }
+    if (what & layer_state_t::eShadowRadiusChanged) {
+        if (layer->setShadowRadius(s.shadowRadius)) flags |= eTraversalNeeded;
+    }
+    if (what & layer_state_t::eFrameRateSelectionPriority) {
+        if (privileged && layer->setFrameRateSelectionPriority(s.frameRateSelectionPriority)) {
+            flags |= eTraversalNeeded;
+        }
+    }
+    if (what & layer_state_t::eFrameRateChanged) {
+        if (layer->setFrameRate(
+                    Layer::FrameRate(s.frameRate, Layer::FrameRateCompatibility::Default)))
+            flags |= eTraversalNeeded;
+    }
+    // This has to happen after we reparent children because when we reparent to null we remove
+    // child layers from current state and remove its relative z. If the children are reparented in
+    // the same transaction, then we have to make sure we reparent the children first so we do not
+    // lose its relative z order.
+    if (what & layer_state_t::eReparent) {
+        bool hadParent = layer->hasParent();
+        if (layer->reparent(s.parentHandleForChild)) {
+            if (!hadParent) {
+                mCurrentState.layersSortedByZ.remove(layer);
+            }
+            flags |= eTransactionNeeded | eTraversalNeeded;
+        }
+    }
     std::vector<sp<CallbackHandle>> callbackHandles;
-    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) {
-        for (const auto& [listener, callbackIds] : listenerCallbacks) {
+    if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) {
+        for (auto& [listener, callbackIds] : s.listeners) {
             callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface));
         }
     }
@@ -4223,7 +3618,8 @@
         buffer = s.buffer;
     }
     if (buffer) {
-        if (layer->setBuffer(buffer, postTime, desiredPresentTime, s.cachedBuffer)) {
+        if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime,
+                             s.cachedBuffer)) {
             flags |= eTraversalNeeded;
         }
     }
@@ -4247,12 +3643,41 @@
     return flags;
 }
 
+status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+                                     sp<IBinder>* outHandle) {
+    if (!mirrorFromHandle) {
+        return NAME_NOT_FOUND;
+    }
+
+    sp<Layer> mirrorLayer;
+    sp<Layer> mirrorFrom;
+    std::string uniqueName = getUniqueLayerName("MirrorRoot");
+
+    {
+        Mutex::Autolock _l(mStateLock);
+        mirrorFrom = fromHandle(mirrorFromHandle);
+        if (!mirrorFrom) {
+            return NAME_NOT_FOUND;
+        }
+
+        status_t result = createContainerLayer(client, std::move(uniqueName), -1, -1, 0,
+                                               LayerMetadata(), outHandle, &mirrorLayer);
+        if (result != NO_ERROR) {
+            return result;
+        }
+
+        mirrorLayer->mClonedChild = mirrorFrom->createClone();
+    }
+
+    return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false);
+}
+
 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,
-                                     const sp<IBinder>& parentHandle,
-                                     const sp<Layer>& parentLayer) {
+                                     const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
+                                     uint32_t* outTransformHint) {
     if (int32_t(w|h) < 0) {
         ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                 int(w), int(h));
@@ -4267,7 +3692,7 @@
 
     sp<Layer> layer;
 
-    String8 uniqueName = getUniqueLayerName(name);
+    std::string uniqueName = getUniqueLayerName(name.string());
 
     bool primaryDisplayOnly = false;
 
@@ -4283,15 +3708,15 @@
 
     switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
         case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            result = createBufferQueueLayer(client, uniqueName, w, h, flags, std::move(metadata),
-                                            format, handle, gbp, &layer);
+            result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags,
+                                            std::move(metadata), format, handle, gbp, &layer);
 
             break;
         case ISurfaceComposerClient::eFXSurfaceBufferState:
-            result = createBufferStateLayer(client, uniqueName, w, h, flags, std::move(metadata),
-                                            handle, &layer);
+            result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
+                                            std::move(metadata), handle, outTransformHint, &layer);
             break;
-        case ISurfaceComposerClient::eFXSurfaceColor:
+        case ISurfaceComposerClient::eFXSurfaceEffect:
             // 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)",
@@ -4299,8 +3724,8 @@
                 return BAD_VALUE;
             }
 
-            result = createColorLayer(client, uniqueName, w, h, flags, std::move(metadata), handle,
-                                      &layer);
+            result = createEffectLayer(client, std::move(uniqueName), w, h, flags,
+                                       std::move(metadata), handle, &layer);
             break;
         case ISurfaceComposerClient::eFXSurfaceContainer:
             // check if buffer size is set for container layer.
@@ -4309,8 +3734,8 @@
                       int(w), int(h));
                 return BAD_VALUE;
             }
-            result = createContainerLayer(client, uniqueName, w, h, flags, std::move(metadata),
-                                          handle, &layer);
+            result = createContainerLayer(client, std::move(uniqueName), w, h, flags,
+                                          std::move(metadata), handle, &layer);
             break;
         default:
             result = BAD_VALUE;
@@ -4337,35 +3762,32 @@
     return result;
 }
 
-String8 SurfaceFlinger::getUniqueLayerName(const String8& name)
-{
-    bool matchFound = true;
-    uint32_t dupeCounter = 0;
+std::string SurfaceFlinger::getUniqueLayerName(const char* name) {
+    unsigned dupeCounter = 0;
 
     // Tack on our counter whether there is a hit or not, so everyone gets a tag
-    String8 uniqueName = name + "#" + String8(std::to_string(dupeCounter).c_str());
+    std::string uniqueName = base::StringPrintf("%s#%u", name, dupeCounter);
 
     // Grab the state lock since we're accessing mCurrentState
     Mutex::Autolock lock(mStateLock);
 
     // Loop over layers until we're sure there is no matching name
+    bool matchFound = true;
     while (matchFound) {
         matchFound = false;
-        mCurrentState.traverseInZOrder([&](Layer* layer) {
+        mCurrentState.traverse([&](Layer* layer) {
             if (layer->getName() == uniqueName) {
                 matchFound = true;
-                uniqueName = name + "#" + String8(std::to_string(++dupeCounter).c_str());
+                uniqueName = base::StringPrintf("%s#%u", name, ++dupeCounter);
             }
         });
     }
 
-    ALOGV_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, uniqueName.c_str());
     return uniqueName;
 }
 
-status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name,
+status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::string name,
                                                 uint32_t w, uint32_t h, uint32_t flags,
                                                 LayerMetadata metadata, PixelFormat& format,
                                                 sp<IBinder>* handle,
@@ -4382,8 +3804,17 @@
         break;
     }
 
-    sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+    sp<BufferQueueLayer> layer;
+    LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
+    args.textureName = getNewTexture();
+    {
+        // Grab the SF state lock during this since it's the only safe way to access
+        // RenderEngine when creating a BufferLayerConsumer
+        // TODO: Check if this lock is still needed here
+        Mutex::Autolock lock(mStateLock);
+        layer = getFactory().createBufferQueueLayer(args);
+    }
+
     status_t err = layer->setDefaultBufferProperties(w, h, format);
     if (err == NO_ERROR) {
         *handle = layer->getHandle();
@@ -4395,38 +3826,42 @@
     return err;
 }
 
-status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name,
+status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
                                                 uint32_t w, uint32_t h, uint32_t flags,
                                                 LayerMetadata metadata, sp<IBinder>* handle,
-                                                sp<Layer>* outLayer) {
-    sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+                                                uint32_t* outTransformHint, sp<Layer>* outLayer) {
+    LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
+    args.displayDevice = getDefaultDisplayDevice();
+    args.textureName = getNewTexture();
+    sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
+    if (outTransformHint) {
+        *outTransformHint = layer->getTransformHint();
+    }
     *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, LayerMetadata metadata,
-                                          sp<IBinder>* handle, sp<Layer>* outLayer) {
-    *outLayer = getFactory().createColorLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+status_t SurfaceFlinger::createEffectLayer(const sp<Client>& client, std::string name, uint32_t w,
+                                           uint32_t h, uint32_t flags, LayerMetadata metadata,
+                                           sp<IBinder>* handle, sp<Layer>* outLayer) {
+    *outLayer = getFactory().createEffectLayer(
+            {this, client, std::move(name), w, h, flags, std::move(metadata)});
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, const String8& name,
+status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, std::string name,
                                               uint32_t w, uint32_t h, uint32_t flags,
                                               LayerMetadata metadata, sp<IBinder>* handle,
                                               sp<Layer>* outLayer) {
     *outLayer = getFactory().createContainerLayer(
-            LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)));
+            {this, client, std::move(name), w, h, flags, std::move(metadata)});
     *handle = (*outLayer)->getHandle();
     return NO_ERROR;
 }
 
-
 void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) {
     mLayersPendingRemoval.add(layer);
     mLayersRemoved = true;
@@ -4474,13 +3909,14 @@
              DisplayState::eLayerStackChanged;
     d.token = token;
     d.layerStack = 0;
-    d.orientation = DisplayState::eOrientationDefault;
+    d.orientation = ui::ROTATION_0;
     d.frame.makeInvalid();
     d.viewport.makeInvalid();
     d.width = 0;
     d.height = 0;
     displays.add(d);
-    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});
+    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false,
+                        {});
 
     setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
 
@@ -4529,7 +3965,6 @@
     }
 
     if (currentMode == HWC_POWER_MODE_OFF) {
-        // Turn on the display
         getHwComposer().setPowerMode(*displayId, mode);
         if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
             setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
@@ -4586,7 +4021,7 @@
 
     if (display->isPrimary()) {
         mTimeStats->setPowerMode(mode);
-        mRefreshRateStats.setPowerMode(mode);
+        mRefreshRateStats->setPowerMode(mode);
         mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
     }
 
@@ -4634,17 +4069,11 @@
                           strerror(-err), err);
         }
 
-        using namespace std::string_literals;
-
         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) {
-                         mScheduler->dumpPrimaryDispSync(s);
-                 })},
-                {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })},
-                {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })},
+                {"--dispsync"s,
+                 dumper([this](std::string& s) { mScheduler->getPrimaryDispSync().dump(s); })},
+                {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)},
                 {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)},
                 {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
                 {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
@@ -4657,7 +4086,8 @@
 
         const auto flag = args.empty() ? ""s : std::string(String8(args[0]));
 
-        if (const auto it = dumpers.find(flag); it != dumpers.end()) {
+        const auto it = dumpers.find(flag);
+        if (it != dumpers.end()) {
             (it->second)(args, asProto, result);
         } else if (!asProto) {
             dumpAllLocked(args, result);
@@ -4667,13 +4097,17 @@
             mStateLock.unlock();
         }
 
-        LayersProto layersProto = dumpProtoFromMainThread();
-        if (asProto) {
-            result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
-        } else {
-            auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
-            result.append(LayerProtoParser::layerTreeToString(layerTree));
-            result.append("\n");
+        if (it == dumpers.end()) {
+            const LayersProto layersProto = dumpProtoFromMainThread();
+            if (asProto) {
+                result.append(layersProto.SerializeAsString());
+            } else {
+                // Dump info that we need to access from the main thread
+                const auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+                result.append(LayerProtoParser::layerTreeToString(layerTree));
+                result.append("\n");
+                dumpOffscreenLayers(result);
+            }
         }
     }
     write(fd, result.c_str(), result.size());
@@ -4690,7 +4124,7 @@
 
 void SurfaceFlinger::listLayersLocked(std::string& result) const {
     mCurrentState.traverseInZOrder(
-            [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
+            [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getDebugName()); });
 }
 
 void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
@@ -4699,7 +4133,7 @@
     if (args.size() > 1) {
         const auto name = String8(args[1]);
         mCurrentState.traverseInZOrder([&](Layer* layer) {
-            if (name == layer->getName()) {
+            if (layer->getName() == name.string()) {
                 layer->dumpFrameStats(result);
             }
         });
@@ -4709,8 +4143,11 @@
 }
 
 void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        if (args.size() < 2 || String8(args[1]) == layer->getName()) {
+    const bool clearAll = args.size() < 2;
+    const auto name = clearAll ? String8() : String8(args[1]);
+
+    mCurrentState.traverse([&](Layer* layer) {
+        if (clearAll || layer->getName() == name.string()) {
             layer->clearFrameStats();
         }
     });
@@ -4725,11 +4162,11 @@
 // 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() {
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
+    mDrawingState.traverse([&](Layer* layer) {
         layer->logFrameStats();
     });
 
-    mAnimFrameTracker.logAndResetStats(String8("<win-anim>"));
+    mAnimFrameTracker.logAndResetStats("<win-anim>");
 }
 
 void SurfaceFlinger::appendSfConfigString(std::string& result) const {
@@ -4748,24 +4185,26 @@
 }
 
 void SurfaceFlinger::dumpVSync(std::string& result) const {
-    mPhaseOffsets->dump(result);
+    mScheduler->dump(result);
+
+    mRefreshRateStats->dump(result);
+    result.append("\n");
+
+    mPhaseConfiguration->dump(result);
     StringAppendF(&result,
-                  "    present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
+                  "      present offset: %9" PRId64 " ns\t     VSYNC period: %9" PRId64 " ns\n\n",
                   dispSyncPresentTimeOffset, getVsyncPeriod());
 
-    StringAppendF(&result, "Scheduler enabled.");
-    StringAppendF(&result, "+  Smart 90 for video detection: %s\n\n",
-                  mUseSmart90ForVideo ? "on" : "off");
-    StringAppendF(&result, "Allowed Display Configs: ");
-    for (int32_t configId : mAllowedDisplayConfigs) {
-        for (auto refresh : mRefreshRateConfigs.getRefreshRates()) {
-            if (refresh.second && refresh.second->configId == configId) {
-                StringAppendF(&result, "%dHz, ", refresh.second->fps);
-            }
-        }
-    }
+    HwcConfigIndexType defaultConfig;
+    float minFps, maxFps;
+    mRefreshRateConfigs->getPolicy(&defaultConfig, &minFps, &maxFps);
+    StringAppendF(&result,
+                  "DesiredDisplayConfigSpecs: default config ID: %d"
+                  ", min: %.2f Hz, max: %.2f Hz",
+                  defaultConfig.value(), minFps, maxFps);
     StringAppendF(&result, "(config override by backdoor: %s)\n\n",
                   mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
+
     mScheduler->dump(mAppConnectionHandle, result);
 }
 
@@ -4784,8 +4223,8 @@
                   bucketTimeSec, percent);
 }
 
-void SurfaceFlinger::recordBufferingStats(const char* layerName,
-        std::vector<OccupancyTracker::Segment>&& history) {
+void SurfaceFlinger::recordBufferingStats(const std::string& layerName,
+                                          std::vector<OccupancyTracker::Segment>&& history) {
     Mutex::Autolock lock(getBE().mBufferingStatsMutex);
     auto& stats = getBE().mBufferingStats[layerName];
     for (const auto& segment : history) {
@@ -4866,21 +4305,13 @@
         }
 
         if (!isEdid(data)) {
-            result.append("unknown identification data: ");
-            for (uint8_t byte : data) {
-                StringAppendF(&result, "%x ", byte);
-            }
-            result.append("\n");
+            result.append("unknown identification data\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");
+            result.append("invalid EDID\n");
             continue;
         }
 
@@ -4890,6 +4321,18 @@
     }
 }
 
+void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args,
+                                                      std::string& result) const {
+    hwc2_display_t hwcDisplayId;
+    uint8_t port;
+    DisplayIdentificationData data;
+
+    if (args.size() > 1 && base::ParseUint(String8(args[1]), &hwcDisplayId) &&
+        getHwComposer().getDisplayIdentificationData(hwcDisplayId, &port, &data)) {
+        result.append(reinterpret_cast<const char*>(data.data()), data.size());
+    }
+}
+
 void SurfaceFlinger::dumpWideColorInfo(std::string& result) const {
     StringAppendF(&result, "Device has wide color built-in display: %d\n", hasWideColorDisplay);
     StringAppendF(&result, "Device uses color management: %d\n", useColorManagement);
@@ -4919,46 +4362,46 @@
 
 LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
     LayersProto layersProto;
-    mDrawingState.traverseInZOrder([&](Layer* layer) {
-        LayerProto* layerProto = layersProto.add_layers();
-        layer->writeToProtoDrawingState(layerProto, traceFlags);
-        layer->writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
-    });
-
+    for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
+        layer->writeToProto(layersProto, traceFlags);
+    }
     return layersProto;
 }
 
+void SurfaceFlinger::dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t traceFlags) const {
+    // Add a fake invisible root layer to the proto output and parent all the offscreen layers to
+    // it.
+    LayerProto* rootProto = layersProto.add_layers();
+    const int32_t offscreenRootLayerId = INT32_MAX - 2;
+    rootProto->set_id(offscreenRootLayerId);
+    rootProto->set_name("Offscreen Root");
+    rootProto->set_parent(-1);
+
+    for (Layer* offscreenLayer : mOffscreenLayers) {
+        // Add layer as child of the fake root
+        rootProto->add_children(offscreenLayer->sequence);
+
+        // Add layer
+        LayerProto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags);
+        layerProto->set_parent(offscreenRootLayerId);
+    }
+}
+
 LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
     LayersProto layersProto;
     postMessageSync(new LambdaMessage([&]() { layersProto = dumpDrawingStateProto(traceFlags); }));
     return layersProto;
 }
 
-LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(
-        const sp<DisplayDevice>& displayDevice) const {
-    LayersProto layersProto;
-
-    SizeProto* resolution = layersProto.mutable_resolution();
-    resolution->set_w(displayDevice->getWidth());
-    resolution->set_h(displayDevice->getHeight());
-
-    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() && !display->getOutputLayersOrderedByZ().empty()) {
-            LayerProto* layerProto = layersProto.add_layers();
-            layer->writeToProtoCompositionState(layerProto, displayDevice);
+void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
+    result.append("Offscreen Layers:\n");
+    postMessageSync(new LambdaMessage([&]() {
+        for (Layer* offscreenLayer : mOffscreenLayers) {
+            offscreenLayer->traverse(LayerVector::StateSet::Drawing, [&](Layer* layer) {
+                layer->dumpCallingUidPid(result);
+            });
         }
-    });
-
-    return layersProto;
+    }));
 }
 
 void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
@@ -4995,7 +4438,7 @@
     result.append("\n\n");
 
     colorizer.bold(result);
-    result.append("VSYNC configuration:\n");
+    result.append("Scheduler:\n");
     colorizer.reset(result);
     dumpVSync(result);
     result.append("\n");
@@ -5013,7 +4456,7 @@
      * Dump the visible layer list
      */
     colorizer.bold(result);
-    StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers);
+    StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
     StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n",
                   mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
     colorizer.reset(result);
@@ -5021,8 +4464,13 @@
     {
         StringAppendF(&result, "Composition layers\n");
         mDrawingState.traverseInZOrder([&](Layer* layer) {
-            auto compositionLayer = layer->getCompositionLayer();
-            if (compositionLayer) compositionLayer->dump(result);
+            auto* compositionState = layer->getCompositionState();
+            if (!compositionState) return;
+
+            android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer,
+                                         layer->getDebugName() ? layer->getDebugName()
+                                                               : "<unknown>");
+            compositionState->dump(result);
         });
     }
 
@@ -5039,6 +4487,12 @@
     result.append("\n");
 
     /*
+     * Dump CompositionEngine state
+     */
+
+    mCompositionEngine->dump(result);
+
+    /*
      * Dump SurfaceFlinger global state
      */
 
@@ -5053,8 +4507,8 @@
     if (const auto display = getDefaultDisplayDeviceLocked()) {
         display->getCompositionDisplay()->getState().undefinedRegion.dump(result,
                                                                           "undefinedRegion");
-        StringAppendF(&result, "  orientation=%d, isPoweredOn=%d\n", display->getOrientation(),
-                      display->isPoweredOn());
+        StringAppendF(&result, "  orientation=%s, isPoweredOn=%d\n",
+                      toCString(display->getOrientation()), display->isPoweredOn());
     }
     StringAppendF(&result,
                   "  transaction-flags         : %08x\n"
@@ -5068,8 +4522,8 @@
                       "  refresh-rate              : %f fps\n"
                       "  x-dpi                     : %f\n"
                       "  y-dpi                     : %f\n",
-                      1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(),
-                      activeConfig->getDpiY());
+                      1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId),
+                      activeConfig->getDpiX(), activeConfig->getDpiY());
     }
 
     StringAppendF(&result, "  transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -5122,31 +4576,10 @@
         result.append("\n");
     }
 
-    /**
-     * Scheduler dump state.
-     */
-    result.append("\nScheduler state:\n");
-    result.append(mScheduler->doDump() + "\n");
-    StringAppendF(&result, "+  Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
-    result.append(mRefreshRateStats.doDump() + "\n");
-
     result.append(mTimeStats->miniDump());
     result.append("\n");
 }
 
-const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) {
-    // Note: mStateLock is held here
-    for (const auto& [token, display] : mDisplays) {
-        if (display->getId() == displayId) {
-            return getDisplayDeviceLocked(token)->getVisibleLayersSortedByZ();
-        }
-    }
-
-    ALOGE("%s: Invalid display %s", __FUNCTION__, to_string(displayId).c_str());
-    static const Vector<sp<Layer>> empty;
-    return empty;
-}
-
 void SurfaceFlinger::updateColorMatrixLocked() {
     mat4 colorMatrix;
     if (mGlobalSaturationFactor != 1.0f) {
@@ -5184,16 +4617,20 @@
         case ENABLE_VSYNC_INJECTIONS:
         case GET_ANIMATION_FRAME_STATS:
         case GET_HDR_CAPABILITIES:
-        case SET_ACTIVE_CONFIG:
-        case SET_ALLOWED_DISPLAY_CONFIGS:
-        case GET_ALLOWED_DISPLAY_CONFIGS:
+        case SET_DESIRED_DISPLAY_CONFIG_SPECS:
+        case GET_DESIRED_DISPLAY_CONFIG_SPECS:
         case SET_ACTIVE_COLOR_MODE:
+        case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
+        case SET_AUTO_LOW_LATENCY_MODE:
+        case GET_GAME_CONTENT_TYPE_SUPPORT:
+        case SET_GAME_CONTENT_TYPE:
         case INJECT_VSYNC:
         case SET_POWER_MODE:
         case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
         case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
         case GET_DISPLAYED_CONTENT_SAMPLE:
-        case NOTIFY_POWER_HINT: {
+        case NOTIFY_POWER_HINT:
+        case SET_GLOBAL_SHADOW_SETTINGS: {
             if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
                 IPCThreadState* ipc = IPCThreadState::self();
                 ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5224,7 +4661,9 @@
         case GET_PHYSICAL_DISPLAY_TOKEN:
         case GET_DISPLAY_COLOR_MODES:
         case GET_DISPLAY_NATIVE_PRIMARIES:
+        case GET_DISPLAY_INFO:
         case GET_DISPLAY_CONFIGS:
+        case GET_DISPLAY_STATE:
         case GET_DISPLAY_STATS:
         case GET_SUPPORTED_FRAME_TIMESTAMPS:
         // Calling setTransactionState is safe, because you need to have been
@@ -5254,12 +4693,6 @@
             }
             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;
-        }
         case CAPTURE_SCREEN_BY_ID: {
             IPCThreadState* ipc = IPCThreadState::self();
             const int uid = ipc->getCallingUid();
@@ -5415,13 +4848,8 @@
                 updateColorMatrixLocked();
                 return NO_ERROR;
             }
-            // This is an experimental interface
-            // Needs to be shifted to proper binder interface when we productize
-            case 1016: {
-                n = data.readInt32();
-                // TODO(b/113612090): Evaluate if this can be removed.
-                mScheduler->setRefreshSkipCount(n);
-                return NO_ERROR;
+            case 1016: { // Unused.
+                return NAME_NOT_FOUND;
             }
             case 1017: {
                 n = data.readInt32();
@@ -5515,13 +4943,13 @@
 
                 DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32());
                 switch (setting) {
-                    case DisplayColorSetting::MANAGED:
+                    case DisplayColorSetting::kManaged:
                         reply->writeBool(useColorManagement);
                         break;
-                    case DisplayColorSetting::UNMANAGED:
+                    case DisplayColorSetting::kUnmanaged:
                         reply->writeBool(true);
                         break;
-                    case DisplayColorSetting::ENHANCED:
+                    case DisplayColorSetting::kEnhanced:
                         reply->writeBool(display->hasRenderIntent(RenderIntent::ENHANCE));
                         break;
                     default: // vendor display color setting
@@ -5597,18 +5025,15 @@
                 return NO_ERROR;
             }
             case 1034: {
-                // TODO(b/129297325): expose this via developer menu option
                 n = data.readInt32();
-                if (n && !mRefreshRateOverlay) {
-                    RefreshRateType type;
-                    {
-                        std::lock_guard<std::mutex> lock(mActiveConfigLock);
-                        type = mDesiredActiveConfig.type;
-                    }
+                if (n == 1 && !mRefreshRateOverlay) {
                     mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
-                    mRefreshRateOverlay->changeRefreshRate(type);
-                } else if (!n) {
+                    auto current = mRefreshRateConfigs->getCurrentRefreshRate();
+                    mRefreshRateOverlay->changeRefreshRate(current);
+                } else if (n == 0) {
                     mRefreshRateOverlay.reset();
+                } else {
+                    reply->writeBool(mRefreshRateOverlay != nullptr);
                 }
                 return NO_ERROR;
             }
@@ -5617,7 +5042,7 @@
                 mDebugDisplayConfigSetByBackdoor = false;
                 if (n >= 0) {
                     const auto displayToken = getInternalDisplayToken();
-                    status_t result = setAllowedDisplayConfigs(displayToken, {n});
+                    status_t result = setActiveConfig(displayToken, n);
                     if (result != NO_ERROR) {
                         return result;
                     }
@@ -5655,17 +5080,19 @@
 
 status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken,
                                        sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers,
-                                       const Dataspace reqDataspace,
-                                       const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
-                                       uint32_t reqWidth, uint32_t reqHeight,
-                                       bool useIdentityTransform,
-                                       ISurfaceComposer::Rotation rotation,
-                                       bool captureSecureLayers) {
+                                       Dataspace reqDataspace, ui::PixelFormat reqPixelFormat,
+                                       const Rect& sourceCrop, uint32_t reqWidth,
+                                       uint32_t reqHeight, bool useIdentityTransform,
+                                       ui::Rotation rotation, bool captureSecureLayers) {
     ATRACE_CALL();
 
     if (!displayToken) return BAD_VALUE;
 
-    auto renderAreaRotation = fromSurfaceComposerRotation(rotation);
+    auto renderAreaRotation = ui::Transform::toRotationFlags(rotation);
+    if (renderAreaRotation == ui::Transform::ROT_INVALID) {
+        ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation));
+        renderAreaRotation = ui::Transform::ROT_0;
+    }
 
     sp<DisplayDevice> display;
     {
@@ -5710,8 +5137,12 @@
     }
     // Couldn't find display by displayId. Try to get display by layerStack since virtual displays
     // may not have a displayId.
+    return getDisplayByLayerStack(displayOrLayerStack);
+}
+
+const sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
     for (const auto& [token, display] : mDisplays) {
-        if (display->getLayerStack() == displayOrLayerStack) {
+        if (display->getLayerStack() == layerStack) {
             return display;
         }
     }
@@ -5723,7 +5154,7 @@
     sp<DisplayDevice> display;
     uint32_t width;
     uint32_t height;
-    ui::Transform::orientation_flags captureOrientation;
+    ui::Transform::RotationFlags captureOrientation;
     {
         Mutex::Autolock _l(mStateLock);
         display = getDisplayByIdOrLayerStack(displayOrLayerStack);
@@ -5734,12 +5165,25 @@
         width = uint32_t(display->getViewport().width());
         height = uint32_t(display->getViewport().height());
 
-        captureOrientation = fromSurfaceComposerRotation(
-                static_cast<ISurfaceComposer::Rotation>(display->getOrientation()));
-        if (captureOrientation == ui::Transform::orientation_flags::ROT_90) {
-            captureOrientation = ui::Transform::orientation_flags::ROT_270;
-        } else if (captureOrientation == ui::Transform::orientation_flags::ROT_270) {
-            captureOrientation = ui::Transform::orientation_flags::ROT_90;
+        const auto orientation = display->getOrientation();
+        captureOrientation = ui::Transform::toRotationFlags(orientation);
+
+        switch (captureOrientation) {
+            case ui::Transform::ROT_90:
+                captureOrientation = ui::Transform::ROT_270;
+                break;
+
+            case ui::Transform::ROT_270:
+                captureOrientation = ui::Transform::ROT_90;
+                break;
+
+            case ui::Transform::ROT_INVALID:
+                ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation));
+                captureOrientation = ui::Transform::ROT_0;
+                break;
+
+            default:
+                break;
         }
         *outDataspace =
                 pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode);
@@ -5767,8 +5211,8 @@
     public:
         LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
                         int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
-                        bool childrenOnly)
-              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
+                        bool childrenOnly, const Rect& displayViewport)
+              : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
                 mLayer(layer),
                 mCrop(crop),
                 mNeedsFiltering(false),
@@ -5787,7 +5231,7 @@
         }
         bool isSecure() const override { return false; }
         bool needsFiltering() const override { return mNeedsFiltering; }
-        const sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
+        sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; }
         Rect getSourceCrop() const override {
             if (mCrop.isEmpty()) {
                 return getBounds();
@@ -5804,7 +5248,8 @@
                                const Rect& drawingBounds)
                   : oldParent(oldParent), newParent(newParent) {
                 // Compute and cache the bounds for the new parent layer.
-                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform());
+                newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
+                                         0.f /* shadowRadius */);
                 oldParent->setChildrenDrawingParent(newParent);
             }
             ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); }
@@ -5821,10 +5266,14 @@
                 drawLayers();
             } else {
                 Rect bounds = getBounds();
-                screenshotParentLayer = mFlinger->getFactory().createContainerLayer(
-                        LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"),
-                                          bounds.getWidth(), bounds.getHeight(), 0,
-                                          LayerMetadata()));
+                uint32_t w = static_cast<uint32_t>(bounds.getWidth());
+                uint32_t h = static_cast<uint32_t>(bounds.getHeight());
+                // 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 =
+                        mFlinger->getFactory().createContainerLayer({mFlinger, nullptr,
+                                                                     "Screenshot Parent"s, w, h, 0,
+                                                                     LayerMetadata()});
 
                 ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
                 drawLayers();
@@ -5835,9 +5284,6 @@
         const sp<Layer> mLayer;
         const Rect mCrop;
 
-        // 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;
         ui::Transform mTransform;
         bool mNeedsFiltering;
 
@@ -5850,7 +5296,7 @@
     sp<Layer> parent;
     Rect crop(sourceCrop);
     std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
-
+    Rect displayViewport;
     {
         Mutex::Autolock _l(mStateLock);
 
@@ -5867,14 +5313,21 @@
             return PERMISSION_DENIED;
         }
 
+        Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
         if (sourceCrop.width() <= 0) {
             crop.left = 0;
-            crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+            crop.right = parentSourceBounds.getWidth();
         }
 
         if (sourceCrop.height() <= 0) {
             crop.top = 0;
-            crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+            crop.bottom = parentSourceBounds.getHeight();
+        }
+
+        if (crop.isEmpty() || frameScale <= 0.0f) {
+            // Error out if the layer has no source bounds (i.e. they are boundless) and a source
+            // crop was not specified, or an invalid frame scale was provided.
+            return BAD_VALUE;
         }
         reqWidth = crop.width() * frameScale;
         reqHeight = crop.height() * frameScale;
@@ -5888,6 +5341,13 @@
                 return NAME_NOT_FOUND;
             }
         }
+
+        auto display = getDisplayByLayerStack(parent->getLayerStack());
+        if (!display) {
+            return BAD_VALUE;
+        }
+
+        displayViewport = display->getViewport();
     } // mStateLock
 
     // really small crop or frameScale
@@ -5898,7 +5358,8 @@
         reqHeight = 1;
     }
 
-    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
+    LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
+                               displayViewport);
     auto traverseLayers = [parent, childrenOnly,
                            &excludeLayers](const LayerVector::Visitor& visitor) {
         parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -6024,9 +5485,10 @@
     const auto rotation = renderArea.getRotationFlags();
     const auto transform = renderArea.getTransform();
     const auto sourceCrop = renderArea.getSourceCrop();
+    const auto& displayViewport = renderArea.getDisplayViewport();
 
     renderengine::DisplaySettings clientCompositionDisplay;
-    std::vector<renderengine::LayerSettings> clientCompositionLayers;
+    std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers;
 
     // assume that bounds are never offset, and that they are the same as the
     // buffer bounds.
@@ -6087,7 +5549,7 @@
 
     const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill());
 
-    renderengine::LayerSettings fillLayer;
+    compositionengine::LayerFE::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);
@@ -6096,21 +5558,43 @@
 
     Region clearRegion = Region::INVALID_REGION;
     traverseLayers([&](Layer* layer) {
-        renderengine::LayerSettings layerSettings;
-        bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion,
-                                                  false, layerSettings);
-        if (prepared) {
-            clientCompositionLayers.push_back(layerSettings);
-        }
+        const bool supportProtectedContent = false;
+        Region clip(renderArea.getBounds());
+        compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
+                clip,
+                useIdentityTransform,
+                layer->needsFiltering(renderArea.getDisplayDevice()) || renderArea.needsFiltering(),
+                renderArea.isSecure(),
+                supportProtectedContent,
+                clearRegion,
+                displayViewport,
+                clientCompositionDisplay.outputDataspace,
+                true,  /* realContentIsVisible */
+                false, /* clearContent */
+        };
+        std::vector<compositionengine::LayerFE::LayerSettings> results =
+                layer->prepareClientCompositionList(targetSettings);
+        clientCompositionLayers.insert(clientCompositionLayers.end(),
+                                       std::make_move_iterator(results.begin()),
+                                       std::make_move_iterator(results.end()));
+        results.clear();
+
     });
 
+    std::vector<const renderengine::LayerSettings*> clientCompositionLayerPointers;
+    clientCompositionLayers.reserve(clientCompositionLayers.size());
+    std::transform(clientCompositionLayers.begin(), clientCompositionLayers.end(),
+                   std::back_inserter(clientCompositionLayerPointers),
+                   [](compositionengine::LayerFE::LayerSettings& settings)
+                           -> renderengine::LayerSettings* { return &settings; });
+
     clientCompositionDisplay.clearRegion = clearRegion;
     // Use an empty fence for the buffer fence, since we just created the buffer so
     // there is no need for synchronization with the GPU.
     base::unique_fd bufferFence;
     base::unique_fd drawFence;
     getRenderEngine().useProtectedContext(false);
-    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer,
+    getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer,
                                  /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence);
 
     *outSyncFd = drawFence.release();
@@ -6148,6 +5632,10 @@
 
 // ---------------------------------------------------------------------------
 
+void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const {
+    layersSortedByZ.traverse(visitor);
+}
+
 void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const {
     layersSortedByZ.traverseInZOrder(stateSet, visitor);
 }
@@ -6177,45 +5665,39 @@
     }
 }
 
-void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
-                                                      const std::vector<int32_t>& allowedConfigs) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
+                                                              HwcConfigIndexType defaultConfig,
+                                                              float minRefreshRate,
+                                                              float maxRefreshRate) {
+    Mutex::Autolock lock(mStateLock);
+
     if (!display->isPrimary()) {
-        return;
-    }
+        // TODO(b/144711714): For non-primary displays we should be able to set an active config
+        // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
+        // it should go thru setDesiredActiveConfig, similar to primary display.
+        ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
+        const auto displayId = display->getId();
+        LOG_ALWAYS_FATAL_IF(!displayId);
 
-    const auto allowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(),
-                                                      allowedConfigs.end());
-    if (allowedDisplayConfigs == mAllowedDisplayConfigs) {
-        return;
-    }
+        HWC2::VsyncPeriodChangeConstraints constraints;
+        constraints.desiredTimeNanos = systemTime();
+        constraints.seamlessRequired = false;
 
-    ALOGV("Updating allowed configs");
-    mAllowedDisplayConfigs = std::move(allowedDisplayConfigs);
-
-    // TODO(b/140204874): This hack triggers a notification that something has changed, so
-    // that listeners that care about a change in allowed configs can get the notification.
-    // Giving current ActiveConfig so that most other listeners would just drop the event
-    mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
-                                display->getActiveConfig());
-
-    // Set the highest allowed config by iterating backwards on available refresh rates
-    const auto& refreshRates = mRefreshRateConfigs.getRefreshRates();
-    for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
-        if (iter->second && isDisplayConfigAllowed(iter->second->configId)) {
-            ALOGV("switching to config %d", iter->second->configId);
-            setDesiredActiveConfig(
-                    {iter->first, iter->second->configId, Scheduler::ConfigEvent::Changed});
-            break;
+        HWC2::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
+        if (getHwComposer().setActiveConfigWithConstraints(*displayId, defaultConfig.value(),
+                                                           constraints, &timeline) < 0) {
+            return BAD_VALUE;
         }
-    }
-}
+        if (timeline.refreshRequired) {
+            repaintEverythingForHWC();
+        }
 
-status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                  const std::vector<int32_t>& allowedConfigs) {
-    ATRACE_CALL();
-
-    if (!displayToken || allowedConfigs.empty()) {
-        return BAD_VALUE;
+        display->setActiveConfig(defaultConfig);
+        const nsecs_t vsyncPeriod =
+                getHwComposer().getConfigs(*displayId)[defaultConfig.value()]->getVsyncPeriod();
+        mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, defaultConfig,
+                                    vsyncPeriod);
+        return NO_ERROR;
     }
 
     if (mDebugDisplayConfigSetByBackdoor) {
@@ -6223,42 +5705,108 @@
         return NO_ERROR;
     }
 
-    postMessageSync(new LambdaMessage([&]() {
-        const auto display = getDisplayDeviceLocked(displayToken);
-        if (!display) {
-            ALOGE("Attempt to set allowed display configs for invalid display token %p",
-                  displayToken.get());
-        } else if (display->isVirtual()) {
-            ALOGW("Attempt to set allowed display configs for virtual display");
-        } else {
-            Mutex::Autolock lock(mStateLock);
-            setAllowedDisplayConfigsInternal(display, allowedConfigs);
-        }
-    }));
+    bool policyChanged;
+    if (mRefreshRateConfigs->setPolicy(defaultConfig, minRefreshRate, maxRefreshRate,
+                                       &policyChanged) < 0) {
+        return BAD_VALUE;
+    }
+    if (!policyChanged) {
+        return NO_ERROR;
+    }
+
+    ALOGV("Setting desired display config specs: defaultConfig: %d min: %.f max: %.f",
+          defaultConfig.value(), minRefreshRate, maxRefreshRate);
+
+    // TODO(b/140204874): This hack triggers a notification that something has changed, so
+    // that listeners that care about a change in allowed configs can get the notification.
+    // Giving current ActiveConfig so that most other listeners would just drop the event
+    const nsecs_t vsyncPeriod =
+            mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()).vsyncPeriod;
+    mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
+                                display->getActiveConfig(), vsyncPeriod);
+
+    auto configId = mScheduler->getPreferredConfigId();
+    auto preferredRefreshRate = configId
+            ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
+            // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
+            : mRefreshRateConfigs->getRefreshRateFromConfigId(defaultConfig);
+    ALOGV("trying to switch to Scheduler preferred config %d (%s)",
+          preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
+
+    if (isDisplayConfigAllowed(preferredRefreshRate.configId)) {
+        ALOGV("switching to Scheduler preferred config %d", preferredRefreshRate.configId.value());
+        setDesiredActiveConfig({preferredRefreshRate.configId, Scheduler::ConfigEvent::Changed});
+    } else {
+        LOG_ALWAYS_FATAL("Desired config not allowed: %d", preferredRefreshRate.configId.value());
+    }
 
     return NO_ERROR;
 }
 
-status_t SurfaceFlinger::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                                  std::vector<int32_t>* outAllowedConfigs) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                      int32_t defaultConfig, float minRefreshRate,
+                                                      float maxRefreshRate) {
     ATRACE_CALL();
 
-    if (!displayToken || !outAllowedConfigs) {
+    if (!displayToken) {
+        return BAD_VALUE;
+    }
+
+    status_t result = NO_ERROR;
+
+    postMessageSync(new LambdaMessage([&]() {
+        const auto display = getDisplayDeviceLocked(displayToken);
+        if (!display) {
+            result = BAD_VALUE;
+            ALOGE("Attempt to set desired display configs for invalid display token %p",
+                  displayToken.get());
+        } else if (display->isVirtual()) {
+            result = BAD_VALUE;
+            ALOGW("Attempt to set desired display configs for virtual display");
+        } else {
+            result =
+                    setDesiredDisplayConfigSpecsInternal(display, HwcConfigIndexType(defaultConfig),
+                                                         minRefreshRate, maxRefreshRate);
+        }
+    }));
+
+    return result;
+}
+
+status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                                      int32_t* outDefaultConfig,
+                                                      float* outMinRefreshRate,
+                                                      float* outMaxRefreshRate) {
+    ATRACE_CALL();
+
+    if (!displayToken || !outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) {
         return BAD_VALUE;
     }
 
     Mutex::Autolock lock(mStateLock);
-
     const auto display = getDisplayDeviceLocked(displayToken);
     if (!display) {
         return NAME_NOT_FOUND;
     }
 
     if (display->isPrimary()) {
-        outAllowedConfigs->assign(mAllowedDisplayConfigs.begin(), mAllowedDisplayConfigs.end());
+        HwcConfigIndexType defaultConfig;
+        mRefreshRateConfigs->getPolicy(&defaultConfig, outMinRefreshRate, outMaxRefreshRate);
+        *outDefaultConfig = defaultConfig.value();
+        return NO_ERROR;
+    } else if (display->isVirtual()) {
+        return BAD_VALUE;
+    } else {
+        const auto displayId = display->getId();
+        if (!displayId) {
+            return BAD_VALUE;
+        }
+        *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
+        auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
+        *outMinRefreshRate = 1e9f / vsyncPeriod;
+        *outMaxRefreshRate = 1e9f / vsyncPeriod;
+        return NO_ERROR;
     }
-
-    return NO_ERROR;
 }
 
 void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
@@ -6266,7 +5814,10 @@
 }
 
 sp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
-    BBinder *b = handle->localBinder();
+    BBinder* b = nullptr;
+    if (handle) {
+        b = handle->localBinder();
+    }
     if (b == nullptr) {
         return nullptr;
     }
@@ -6277,10 +5828,65 @@
     return nullptr;
 }
 
+void SurfaceFlinger::onLayerFirstRef(Layer* layer) {
+    mNumLayers++;
+    mScheduler->registerLayer(layer);
+}
+
+void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
+    mNumLayers--;
+    removeFromOffscreenLayers(layer);
+}
+
+// WARNING: ONLY CALL THIS FROM LAYER DTOR
+// Here we add children in the current state to offscreen layers and remove the
+// layer itself from the offscreen layer list.  Since
+// this is the dtor, it is safe to access the current state.  This keeps us
+// from dangling children layers such that they are not reachable from the
+// Drawing state nor the offscreen layer list
+// See b/141111965
+void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
+    for (auto& child : layer->getCurrentChildren()) {
+        mOffscreenLayers.emplace(child.get());
+    }
+    mOffscreenLayers.erase(layer);
+}
+
 void SurfaceFlinger::bufferErased(const client_cache_t& clientCacheId) {
     getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
 }
 
+status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+                                                 float lightPosY, float lightPosZ,
+                                                 float lightRadius) {
+    Mutex::Autolock _l(mStateLock);
+    mCurrentState.globalShadowSettings.ambientColor = vec4(ambientColor);
+    mCurrentState.globalShadowSettings.spotColor = vec4(spotColor);
+    mCurrentState.globalShadowSettings.lightPos.y = lightPosY;
+    mCurrentState.globalShadowSettings.lightPos.z = lightPosZ;
+    mCurrentState.globalShadowSettings.lightRadius = lightRadius;
+
+    // these values are overridden when calculating the shadow settings for a layer.
+    mCurrentState.globalShadowSettings.lightPos.x = 0.f;
+    mCurrentState.globalShadowSettings.length = 0.f;
+    return NO_ERROR;
+}
+
+const std::unordered_map<std::string, uint32_t>& SurfaceFlinger::getGenericLayerMetadataKeyMap()
+        const {
+    // TODO(b/149500060): Remove this fixed/static mapping. Please prefer taking
+    // on the work to remove the table in that bug rather than adding more to
+    // it.
+    static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{
+            // Note: METADATA_OWNER_UID and METADATA_WINDOW_TYPE are officially
+            // supported, and exposed via the
+            // IVrComposerClient::VrCommand::SET_LAYER_INFO command.
+            {"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID},
+            {"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR},
+    };
+    return genericLayerMetadataKeyMap;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
@@ -6290,3 +5896,6 @@
 #if defined(__gl2_h_)
 #error "don't include gl2/gl2.h in this file"
 #endif
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8e4203a..0144b4e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -23,18 +23,21 @@
  */
 
 #include <android-base/thread_annotations.h>
+#include <compositionengine/OutputColorSetting.h>
 #include <cutils/atomic.h>
 #include <cutils/compiler.h>
 #include <gui/BufferQueue.h>
 #include <gui/FrameTimestamps.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/ISurfaceComposerClient.h>
+#include <gui/ITransactionCompletedListener.h>
 #include <gui/LayerState.h>
 #include <gui/OccupancyTracker.h>
 #include <hardware/hwcomposer_defs.h>
 #include <input/ISetInputWindowsListener.h>
 #include <layerproto/LayerProtoHeader.h>
 #include <math/mat4.h>
+#include <renderengine/LayerSettings.h>
 #include <serviceutils/PriorityDumper.h>
 #include <system/graphics.h>
 #include <ui/FenceTime.h>
@@ -52,7 +55,6 @@
 #include "DisplayHardware/PowerAdvisor.h"
 #include "Effects/Daltonizer.h"
 #include "FrameTracker.h"
-#include "LayerStats.h"
 #include "LayerVector.h"
 #include "Scheduler/RefreshRateConfigs.h"
 #include "Scheduler/RefreshRateStats.h"
@@ -60,6 +62,7 @@
 #include "Scheduler/VSyncModulator.h"
 #include "SurfaceFlingerFactory.h"
 #include "SurfaceTracing.h"
+#include "TracedOrdinal.h"
 #include "TransactionCompletedThread.h"
 
 #include <atomic>
@@ -68,6 +71,7 @@
 #include <map>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <queue>
 #include <set>
 #include <string>
@@ -86,15 +90,18 @@
 class HWComposer;
 class IGraphicBufferProducer;
 class IInputFlinger;
-class InjectVSyncSource;
 class Layer;
 class MessageBase;
 class RefreshRateOverlay;
 class RegionSamplingThread;
 class TimeStats;
+class FrameTracer;
 
 namespace compositionengine {
 class DisplaySurface;
+class OutputLayer;
+
+struct CompositionRefreshArgs;
 } // namespace compositionengine
 
 namespace renderengine {
@@ -114,11 +121,7 @@
     eTransactionMask = 0x1f,
 };
 
-enum class DisplayColorSetting : int32_t {
-    MANAGED = 0,
-    UNMANAGED = 1,
-    ENHANCED = 2,
-};
+using DisplayColorSetting = compositionengine::OutputColorSetting;
 
 class SurfaceFlingerBE
 {
@@ -172,7 +175,8 @@
                        public PriorityDumper,
                        public ClientCache::ErasedRecipient,
                        private IBinder::DeathRecipient,
-                       private HWC2::ComposerCallback {
+                       private HWC2::ComposerCallback,
+                       private ISchedulerCallback {
 public:
     SurfaceFlingerBE& getBE() { return mBE; }
     const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -225,7 +229,7 @@
     // found on devices with wide color gamut (e.g. Display-P3) display.
     static bool hasWideColorDisplay;
 
-    static int primaryDisplayOrientation;
+    static ui::Rotation internalDisplayOrientation;
 
     // Indicate if device wants color management on its display.
     static bool useColorManagement;
@@ -244,6 +248,11 @@
     static ui::Dataspace wideColorGamutCompositionDataspace;
     static ui::PixelFormat wideColorGamutCompositionPixelFormat;
 
+    // Whether to use frame rate API when deciding about the refresh rate of the display. This
+    // variable is caches in SF, so that we can check it with each layer creation, and a void the
+    // overhead that is caused by reading from sysprop.
+    static bool useFrameRateApi;
+
     static char const* getServiceName() ANDROID_API {
         return "SurfaceFlinger";
     }
@@ -269,9 +278,6 @@
     // 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.
@@ -296,32 +302,22 @@
 
     // main thread function to enable/disable h/w composer event
     void setPrimaryVsyncEnabledInternal(bool enabled);
+    void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled);
 
     // called on the main thread by MessageQueue when an internal message
     // is received
     // TODO: this should be made accessible only to MessageQueue
     void onMessageReceived(int32_t what);
 
-    // populates the expected present time for this frame.
-    // When we are in negative offsets, we perform a correction so that the
-    // predicted vsync for the *next* frame is used instead.
-    void populateExpectedPresentTime();
-    nsecs_t getExpectedPresentTime() const { return mExpectedPresentTime; }
-
-    // for debugging only
-    // TODO: this should be made accessible only to HWComposer
-    const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId);
-
     renderengine::RenderEngine& getRenderEngine() const;
 
     bool authenticateSurfaceTextureLocked(
         const sp<IGraphicBufferProducer>& bufferProducer) const;
 
-    inline void onLayerCreated() { mNumLayers++; }
-    inline void onLayerDestroyed(Layer* layer) {
-        mNumLayers--;
-        mOffscreenLayers.erase(layer);
-    }
+    void onLayerFirstRef(Layer*);
+    void onLayerDestroyed(Layer*);
+
+    void removeFromOffscreenLayers(Layer* layer);
 
     TransactionCompletedThread& getTransactionCompletedThread() {
         return mTransactionCompletedThread;
@@ -332,6 +328,10 @@
     // Inherit from ClientCache::ErasedRecipient
     void bufferErased(const client_cache_t& clientCacheId) override;
 
+    // If set, disables reusing client composition buffers. This can be set by
+    // debug.sf.disable_client_composition_cache
+    bool mDisableClientCompositionCache = false;
+
 private:
     friend class BufferLayer;
     friend class BufferQueueLayer;
@@ -345,6 +345,7 @@
 
     // For unit tests
     friend class TestableSurfaceFlinger;
+    friend class TransactionApplicationTest;
 
     // This value is specified in number of frames.  Log frame stats at most
     // every half hour.
@@ -353,9 +354,11 @@
     static const size_t MAX_LAYERS = 4096;
     static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
 
+protected:
     // We're reference counted, never destroy SurfaceFlinger directly
     virtual ~SurfaceFlinger();
 
+private:
     /* ------------------------------------------------------------------------
      * Internal data structures
      */
@@ -372,6 +375,8 @@
             if (colorMatrixChanged) {
                 colorMatrix = other.colorMatrix;
             }
+            globalShadowSettings = other.globalShadowSettings;
+
             return *this;
         }
 
@@ -382,6 +387,9 @@
         bool colorMatrixChanged = true;
         mat4 colorMatrix;
 
+        renderengine::ShadowSettings globalShadowSettings;
+
+        void traverse(const LayerVector::Visitor& visitor) const;
         void traverseInZOrder(const LayerVector::Visitor& visitor) const;
         void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
     };
@@ -406,6 +414,7 @@
                              const sp<IBinder>& applyToken,
                              const InputWindowCommands& inputWindowCommands,
                              int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+                             bool hasListenerCallbacks,
                              const std::vector<ListenerCallbacks>& listenerCallbacks) override;
     void bootFinished() override;
     bool authenticateSurfaceTexture(
@@ -416,10 +425,10 @@
             ISurfaceComposer::ConfigChanged configChanged =
                     ISurfaceComposer::eConfigChangedSuppress) override;
     status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer,
-            bool& outCapturedSecureLayers, const ui::Dataspace reqDataspace,
-            const ui::PixelFormat reqPixelFormat, Rect sourceCrop,
-            uint32_t reqWidth, uint32_t reqHeight,
-            bool useIdentityTransform, ISurfaceComposer::Rotation rotation, bool captureSecureLayers) override;
+                           bool& outCapturedSecureLayers, ui::Dataspace reqDataspace,
+                           ui::PixelFormat reqPixelFormat, const Rect& sourceCrop,
+                           uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform,
+                           ui::Rotation rotation, bool captureSecureLayers) override;
     status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace,
                            sp<GraphicBuffer>* outBuffer) override;
     status_t captureLayers(
@@ -430,17 +439,22 @@
             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;
+    status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override;
+    status_t getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo*) override;
+    status_t getDisplayConfigs(const sp<IBinder>& displayToken, Vector<DisplayConfig>*) override;
     int getActiveConfig(const sp<IBinder>& displayToken) override;
-    status_t getDisplayColorModes(const sp<IBinder>& displayToken,
-                                  Vector<ui::ColorMode>* configs) override;
+    status_t getDisplayColorModes(const sp<IBinder>& displayToken, Vector<ui::ColorMode>*) override;
     status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken,
-                                       ui::DisplayPrimaries &primaries);
+                                       ui::DisplayPrimaries&) override;
     ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
     status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+    status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+                                          bool* outSupported) const override;
+    void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
+    status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
+                                       bool* outSupported) const override;
+    void setGameContentType(const sp<IBinder>& displayToken, bool on) 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,
@@ -468,15 +482,17 @@
     status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
                                        const sp<IRegionSamplingListener>& listener) override;
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
-    status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                      const std::vector<int32_t>& allowedConfigs) override;
-    status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
-                                      std::vector<int32_t>* outAllowedConfigs) override;
+    status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
+                                          float minRefreshRate, float maxRefreshRate) override;
+    status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+                                          int32_t* outDefaultConfig, float* outMinRefreshRate,
+                                          float* outMaxRefreshRate) override;
     status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                          bool* outSupport) const override;
     status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override;
     status_t notifyPowerHint(int32_t hintId) override;
-
+    status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+                                     float lightPosY, float lightPosZ, float lightRadius) override;
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
      */
@@ -490,13 +506,23 @@
     /* ------------------------------------------------------------------------
      * HWC2::ComposerCallback / HWComposer::EventHandler interface
      */
-    void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
-                         int64_t timestamp) override;
+    void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, int64_t timestamp,
+                         std::optional<hwc2_vsync_period_t> vsyncPeriod) override;
     void onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                            HWC2::Connection connection) override;
     void onRefreshReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId) override;
+    void onVsyncPeriodTimingChangedReceived(
+            int32_t sequenceId, hwc2_display_t display,
+            const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
+    void onSeamlessPossible(int32_t sequenceId, hwc2_display_t display) override;
 
     /* ------------------------------------------------------------------------
+     * ISchedulerCallback
+     */
+    void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+    // force full composition on all displays without resetting the scheduler idle timer.
+    void repaintEverythingForHWC() override;
+    /* ------------------------------------------------------------------------
      * Message handling
      */
     void waitForEvent();
@@ -506,15 +532,14 @@
     void signalLayerUpdate();
     void signalRefresh();
 
-    using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+    using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
 
     struct ActiveConfigInfo {
-        RefreshRateType type = RefreshRateType::DEFAULT;
-        int configId = 0;
+        HwcConfigIndexType configId;
         Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
 
         bool operator!=(const ActiveConfigInfo& other) const {
-            return type != other.type || configId != other.configId || event != other.event;
+            return configId != other.configId || event != other.event;
         }
     };
 
@@ -522,6 +547,7 @@
     void onInitializeDisplays() REQUIRES(mStateLock);
     // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig.
     void setDesiredActiveConfig(const ActiveConfigInfo& info) REQUIRES(mStateLock);
+    status_t setActiveConfig(const sp<IBinder>& displayToken, int id);
     // Once HWC has returned the present fence, this sets the active config and a new refresh
     // rate in SF.
     void setActiveConfigInternal() REQUIRES(mStateLock);
@@ -535,10 +561,16 @@
     // called on the main thread in response to setPowerMode()
     void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
 
-    // called on the main thread in response to setAllowedDisplayConfigs()
-    void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
-                                          const std::vector<int32_t>& allowedConfigs)
-            REQUIRES(mStateLock);
+    // Sets the desired display configs.
+    status_t setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
+                                                  HwcConfigIndexType defaultConfig,
+                                                  float minRefreshRate, float maxRefreshRate)
+            EXCLUDES(mStateLock);
+
+    // called on the main thread in response to setAutoLowLatencyMode()
+    void setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on);
+    // called on the main thread in response to setGameContentType()
+    void setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on);
 
     // Returns whether the transaction actually modified any state
     bool handleMessageTransaction();
@@ -556,6 +588,7 @@
     void commitInputWindowCommands() REQUIRES(mStateLock);
     void setInputWindowsFinished();
     void updateCursorAsync();
+    void initScheduler(DisplayId primaryDisplayId);
 
     /* handlePageFlip - latch a new buffer if available and compute the dirty
      * region. Returns whether a new buffer has been latched, i.e., whether it
@@ -570,10 +603,10 @@
                                const Vector<DisplayState>& displays, uint32_t flags,
                                const InputWindowCommands& inputWindowCommands,
                                const int64_t desiredPresentTime,
-                               const client_cache_t& uncacheBuffer,
+                               const client_cache_t& uncacheBuffer, const int64_t postTime,
+                               bool privileged, bool hasListenerCallbacks,
                                const std::vector<ListenerCallbacks>& listenerCallbacks,
-                               const int64_t postTime, bool privileged, bool isMainThread = false)
-            REQUIRES(mStateLock);
+                               bool isMainThread = false) REQUIRES(mStateLock);
     // Returns true if at least one transaction was flushed
     bool flushTransactionQueues();
     // Returns true if there is at least one transaction that needs to be flushed
@@ -583,45 +616,55 @@
     // Can only be called from the main thread or with mStateLock held
     uint32_t setTransactionFlags(uint32_t flags);
     uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
-    void latchAndReleaseBuffer(const sp<Layer>& layer);
     void commitTransaction() REQUIRES(mStateLock);
     void commitOffscreenLayers();
-    bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
     bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
+                                       bool useCachedExpectedPresentTime,
                                        const Vector<ComposerState>& states);
-    uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime,
-                                  const std::vector<ListenerCallbacks>& listenerCallbacks,
-                                  int64_t postTime, bool privileged) REQUIRES(mStateLock);
     uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
     uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
             REQUIRES(mStateLock);
 
+protected:
+    virtual uint32_t setClientStateLocked(
+            const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime,
+            bool privileged,
+            std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks)
+            REQUIRES(mStateLock);
+    virtual void commitTransactionLocked();
+
+private:
     /* ------------------------------------------------------------------------
      * Layer management
      */
     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,
-                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr);
+                         const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr,
+                         uint32_t* outTransformHint = nullptr);
 
-    status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w,
+    status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
                                     PixelFormat& format, sp<IBinder>* outHandle,
                                     sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
 
-    status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w,
+    status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
                                     uint32_t h, uint32_t flags, LayerMetadata metadata,
-                                    sp<IBinder>* outHandle, sp<Layer>* outLayer);
+                                    sp<IBinder>* outHandle, uint32_t* outTransformHint,
+                                    sp<Layer>* outLayer);
 
-    status_t createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h,
-                              uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
-                              sp<Layer>* outLayer);
+    status_t createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
+                               uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
+                               sp<Layer>* outLayer);
 
-    status_t createContainerLayer(const sp<Client>& client, const String8& name, uint32_t w,
+    status_t createContainerLayer(const sp<Client>& client, std::string name, uint32_t w,
                                   uint32_t h, uint32_t flags, LayerMetadata metadata,
                                   sp<IBinder>* outHandle, sp<Layer>* outLayer);
 
-    String8 getUniqueLayerName(const String8& name);
+    status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+                         sp<IBinder>* outHandle);
+
+    std::string getUniqueLayerName(const char* name);
 
     // called when all clients have released all their references to
     // this layer meaning it is entirely safe to destroy all
@@ -656,6 +699,7 @@
                                  const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
                                  bool& outCapturedSecureLayers);
     const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
+    const sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack);
     status_t captureScreenImplLocked(const RenderArea& renderArea,
                                      TraverseLayersFunction traverseLayers,
                                      ANativeWindowBuffer* buffer, bool useIdentityTransform,
@@ -748,53 +792,15 @@
      * Compositing
      */
     void invalidateHwcGeometry();
-    void computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion,
-                               Region& opaqueRegion);
 
-    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<DisplayDevice>& display, ui::Dataspace* outHdrDataSpace,
-                                   bool* outIsHdrClientComposition) 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>& display, ui::ColorMode* outMode,
-                       ui::Dataspace* outDataSpace, ui::RenderIntent* outRenderIntent) const;
-
-    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 logLayerStats();
-    void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
-
-    // 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(const sp<DisplayDevice>& display);
     void postFrame();
-    void drawWormhole(const Region& region) const;
 
     /* ------------------------------------------------------------------------
      * Display management
@@ -816,9 +822,16 @@
 
     // Sets the refresh rate by switching active configs, if they are available for
     // the desired refresh rate.
-    void setRefreshRateTo(RefreshRateType, Scheduler::ConfigEvent event) REQUIRES(mStateLock);
+    void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
+            REQUIRES(mStateLock);
 
-    bool isDisplayConfigAllowed(int32_t configId) REQUIRES(mStateLock);
+    bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
+
+    bool previousFrameMissed(int graceTimeMs = 0);
+
+    // Populates the expected present time for this frame. For negative offsets, performs a
+    // correction using the predicted vsync for the next frame instead.
+    void populateExpectedPresentTime();
 
     /*
      * Display identification
@@ -848,9 +861,6 @@
         return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt;
     }
 
-    bool previousFrameMissed(int graceTimeMs = 0);
-    void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled);
-
     /*
      * Debugging & dumpsys
      */
@@ -895,16 +905,19 @@
     // Not const because each Layer needs to query Fences and cache timestamps.
     void dumpFrameEventsLocked(std::string& result);
 
-    void recordBufferingStats(const char* layerName,
-            std::vector<OccupancyTracker::Segment>&& history);
+    void recordBufferingStats(const std::string& layerName,
+                              std::vector<OccupancyTracker::Segment>&& history);
     void dumpBufferingStats(std::string& result) const;
     void dumpDisplayIdentificationData(std::string& result) const;
+    void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
     void dumpWideColorInfo(std::string& result) const;
     LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+    void dumpOffscreenLayersProto(LayersProto& layersProto,
+                                  uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
     LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
             EXCLUDES(mStateLock);
+    void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
     void withTracingLock(std::function<void()> operation) REQUIRES(mStateLock);
-    LayersProto dumpVisibleLayersProtoInfo(const sp<DisplayDevice>& display) const;
 
     bool isLayerTripleBufferingDisabled() const {
         return this->mLayerTripleBufferingDisabled;
@@ -965,13 +978,6 @@
     // constant members (no synchronization needed for access)
     const nsecs_t mBootTime = systemTime();
     bool mGpuToCpuSupported = false;
-    std::unique_ptr<EventThread> mInjectorEventThread;
-    std::unique_ptr<InjectVSyncSource> mVSyncInjector;
-
-    // Calculates correct offsets.
-    VSyncModulator mVsyncModulator;
-    // Keeps track of all available phase offsets for different refresh types.
-    const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
 
     // Can only accessed from the main thread, these members
     // don't need synchronization
@@ -991,6 +997,10 @@
     // Note that it is possible for a frame to be composed via both client and device
     // composition, for example in the case of overlays.
     bool mHadDeviceComposition = false;
+    // True if in the previous frame, the client composition was skipped by reusing the buffer
+    // used in a previous composition. This can happed if the client composition requests
+    // did not change.
+    bool mReusedClientComposition = false;
 
     enum class BootStage {
         BOOTLOADER,
@@ -1026,9 +1036,10 @@
     SurfaceTracing mTracing{*this};
     bool mTracingEnabled = false;
     bool mTracingEnabledChanged GUARDED_BY(mStateLock) = false;
-    LayerStats mLayerStats;
     const std::shared_ptr<TimeStats> mTimeStats;
+    const std::unique_ptr<FrameTracer> mFrameTracer;
     bool mUseHwcVirtualDisplays = false;
+    bool mEnableBlurs = false;
     std::atomic<uint32_t> mFrameMissedCount = 0;
     std::atomic<uint32_t> mHwcFrameMissedCount = 0;
     std::atomic<uint32_t> mGpuFrameMissedCount = 0;
@@ -1057,47 +1068,42 @@
     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, const client_cache_t& uncacheBuffer,
-                         const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime,
-                         bool privileged)
+                         int64_t postTime, bool privileged, bool hasListenerCallbacks,
+                         std::vector<ListenerCallbacks> listenerCallbacks)
               : states(composerStates),
                 displays(displayStates),
                 flags(transactionFlags),
                 desiredPresentTime(desiredPresentTime),
                 buffer(uncacheBuffer),
-                callback(listenerCallbacks),
                 postTime(postTime),
-                privileged(privileged) {}
+                privileged(privileged),
+                hasListenerCallbacks(hasListenerCallbacks),
+                listenerCallbacks(listenerCallbacks) {}
 
         Vector<ComposerState> states;
         Vector<DisplayState> displays;
         uint32_t flags;
         const int64_t desiredPresentTime;
         client_cache_t buffer;
-        std::vector<ListenerCallbacks> callback;
         const int64_t postTime;
         bool privileged;
+        bool hasListenerCallbacks;
+        std::vector<ListenerCallbacks> listenerCallbacks;
     };
-    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues;
+    std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues;
 
     /* ------------------------------------------------------------------------
      * Feature prototyping
      */
 
-    bool mInjectVSyncs = false;
-
     // Static screen stats
     bool mHasPoweredOff = false;
 
-    size_t mNumLayers = 0;
+    std::atomic<size_t> mNumLayers = 0;
 
     // Verify that transaction is being called by an approved process:
     // either AID_GRAPHICS or AID_SYSTEM.
@@ -1111,7 +1117,7 @@
     static bool useVrFlinger;
     std::thread::id mMainThreadId = std::this_thread::get_id();
 
-    DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::ENHANCED;
+    DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::kEnhanced;
 
     // Color mode forced by setting persist.sys.sf.color_mode, it must:
     //     1. not be NATIVE color mode, NATIVE color mode means no forced color mode;
@@ -1132,17 +1138,29 @@
     /* ------------------------------------------------------------------------
      * Scheduler
      */
-    bool mUseSmart90ForVideo = false;
     std::unique_ptr<Scheduler> mScheduler;
-    sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
-    sp<Scheduler::ConnectionHandle> mSfConnectionHandle;
+    scheduler::ConnectionHandle mAppConnectionHandle;
+    scheduler::ConnectionHandle mSfConnectionHandle;
 
-    scheduler::RefreshRateConfigs mRefreshRateConfigs;
-    scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats};
+    // Stores phase offsets configured per refresh rate.
+    std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
 
-    // All configs are allowed if the set is empty.
-    using DisplayConfigs = std::set<int32_t>;
-    DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
+    // Optional to defer construction until scheduler connections are created.
+    std::optional<scheduler::VSyncModulator> mVSyncModulator;
+
+    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats;
+
+    std::atomic<nsecs_t> mExpectedPresentTime = 0;
+
+    /* ------------------------------------------------------------------------
+     * Generic Layer Metadata
+     */
+    const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const;
+
+    /* ------------------------------------------------------------------------
+     * Misc
+     */
 
     std::mutex mActiveConfigLock;
     // This bit is set once we start setting the config. We read from this bit during the
@@ -1153,13 +1171,17 @@
     ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock);
 
     // below flags are set by main thread only
-    bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
+    TracedOrdinal<bool> mDesiredActiveConfigChanged
+            GUARDED_BY(mActiveConfigLock) = {"DesiredActiveConfigChanged", false};
     bool mCheckPendingFence = false;
 
     bool mLumaSampling = true;
     sp<RegionSamplingThread> mRegionSamplingThread;
     ui::DisplayPrimaries mInternalDisplayPrimaries;
 
+    const float mInternalDisplayDensity;
+    const float mEmulatedDisplayDensity;
+
     sp<IInputFlinger> mInputFlinger;
     InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
     // Should only be accessed by the main thread.
@@ -1176,9 +1198,12 @@
 
     const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this);
 
-    bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
+    bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false;
     Hwc2::impl::PowerAdvisor mPowerAdvisor;
 
+    // This should only be accessed on the main thread.
+    nsecs_t mFrameStartTime = 0;
+
     std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
 
     // Flag used to set override allowed display configs from backdoor
@@ -1193,8 +1218,6 @@
     // Flags to capture the state of Vsync in HWC
     HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
     HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
-
-    nsecs_t mExpectedPresentTime;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
new file mode 100644
index 0000000..d49133d
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <compositionengine/impl/CompositionEngine.h>
+#include <cutils/properties.h>
+#include <ui/GraphicBuffer.h>
+
+#include "BufferLayerConsumer.h"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "ContainerLayer.h"
+#include "DisplayDevice.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+#include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlingerDefaultFactory.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"
+
+namespace android::surfaceflinger {
+
+DefaultFactory::~DefaultFactory() = default;
+
+std::unique_ptr<DispSync> DefaultFactory::createDispSync(const char* name, bool hasSyncFramework) {
+    return std::make_unique<android::impl::DispSync>(name, hasSyncFramework);
+}
+
+std::unique_ptr<EventControlThread> DefaultFactory::createEventControlThread(
+        SetVSyncEnabled setVSyncEnabled) {
+    return std::make_unique<android::impl::EventControlThread>(std::move(setVSyncEnabled));
+}
+
+std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
+    return std::make_unique<android::impl::HWComposer>(serviceName);
+}
+
+std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() {
+    return std::make_unique<android::impl::MessageQueue>();
+}
+
+std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+        const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+    if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
+        return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+    } else {
+        return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+    }
+}
+
+std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
+        SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
+        ISchedulerCallback& schedulerCallback) {
+    return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
+                                       property_get_bool("debug.sf.use_content_detection_v2",
+                                                         true));
+}
+
+std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
+        SurfaceFlinger* flinger) {
+    return std::make_unique<android::impl::SurfaceInterceptor>(flinger);
+}
+
+sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
+        bool timestampPropertyValue) {
+    return new StartPropertySetThread(timestampPropertyValue);
+}
+
+sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) {
+    return new DisplayDevice(std::move(creationArgs));
+}
+
+sp<GraphicBuffer> DefaultFactory::createGraphicBuffer(uint32_t width, uint32_t height,
+                                                      PixelFormat format, uint32_t layerCount,
+                                                      uint64_t usage, std::string requestorName) {
+    return new GraphicBuffer(width, height, format, layerCount, usage, requestorName);
+}
+
+void DefaultFactory::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                                       sp<IGraphicBufferConsumer>* outConsumer,
+                                       bool consumerIsSurfaceFlinger) {
+    BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+}
+
+sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer(
+        const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger,
+        const wp<Layer>& layer) {
+    return new MonitoredProducer(producer, flinger, layer);
+}
+
+sp<BufferLayerConsumer> DefaultFactory::createBufferLayerConsumer(
+        const sp<IGraphicBufferConsumer>& consumer, renderengine::RenderEngine& renderEngine,
+        uint32_t textureName, Layer* layer) {
+    return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
+}
+
+std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface(
+        const sp<IGraphicBufferProducer>& producer) {
+    return surfaceflinger::impl::createNativeWindowSurface(producer);
+}
+
+std::unique_ptr<compositionengine::CompositionEngine> DefaultFactory::createCompositionEngine() {
+    return compositionengine::impl::createCompositionEngine();
+}
+
+sp<ContainerLayer> DefaultFactory::createContainerLayer(const LayerCreationArgs& args) {
+    return new ContainerLayer(args);
+}
+
+sp<BufferQueueLayer> DefaultFactory::createBufferQueueLayer(const LayerCreationArgs& args) {
+    return new BufferQueueLayer(args);
+}
+
+sp<BufferStateLayer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) {
+    return new BufferStateLayer(args);
+}
+
+sp<EffectLayer> DefaultFactory::createEffectLayer(const LayerCreationArgs& args) {
+    return new EffectLayer(args);
+}
+
+} // namespace android::surfaceflinger
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
new file mode 100644
index 0000000..89194c7
--- /dev/null
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.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 "SurfaceFlingerFactory.h"
+
+namespace android::surfaceflinger {
+
+// A default implementation of the factory which creates the standard
+// implementation types for each interface.
+class DefaultFactory : public surfaceflinger::Factory {
+public:
+    virtual ~DefaultFactory();
+
+    std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override;
+    std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
+    std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
+    std::unique_ptr<MessageQueue> createMessageQueue() override;
+    std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs&) override;
+    std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
+                                               const scheduler::RefreshRateConfigs&,
+                                               ISchedulerCallback&) override;
+    std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
+    sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
+    sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) override;
+    sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
+                                          uint32_t layerCount, uint64_t usage,
+                                          std::string requestorName) override;
+    void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+                           sp<IGraphicBufferConsumer>* outConsumer,
+                           bool consumerIsSurfaceFlinger) override;
+    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
+                                                       const sp<SurfaceFlinger>&,
+                                                       const wp<Layer>&) override;
+    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
+                                                      renderengine::RenderEngine&, uint32_t tex,
+                                                      Layer*) override;
+    std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
+            const sp<IGraphicBufferProducer>&) override;
+    std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
+    sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override;
+    sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override;
+    sp<EffectLayer> createEffectLayer(const LayerCreationArgs& args) override;
+    sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override;
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp
index e425b2a..3997b04 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp
@@ -14,129 +14,22 @@
  * limitations under the License.
  */
 
-#include <compositionengine/impl/CompositionEngine.h>
-#include <ui/GraphicBuffer.h>
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 
-#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"
+#include "SurfaceFlingerDefaultFactory.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,
-                const scheduler::RefreshRateConfigs& refreshRateConfig) override {
-            return std::make_unique<Scheduler>(callback, refreshRateConfig);
-        }
-
-        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::shared_ptr<TimeStats> createTimeStats() override {
-            return std::make_shared<android::impl::TimeStats>();
-        }
-    };
-    static Factory factory;
+    static DefaultFactory factory;
 
     return new SurfaceFlinger(factory);
 }
 
 } // namespace android::surfaceflinger
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index c2bc808..209bd0c 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,21 +16,22 @@
 
 #pragma once
 
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
 #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 BufferLayerConsumer;
+class EffectLayer;
 class ContainerLayer;
 class DisplayDevice;
 class DispSync;
@@ -39,12 +40,13 @@
 class HWComposer;
 class IGraphicBufferConsumer;
 class IGraphicBufferProducer;
+class ISchedulerCallback;
+class Layer;
 class MessageQueue;
 class Scheduler;
 class StartPropertySetThread;
 class SurfaceFlinger;
 class SurfaceInterceptor;
-class TimeStats;
 
 struct DisplayDeviceCreationArgs;
 struct LayerCreationArgs;
@@ -54,8 +56,10 @@
 } // namespace compositionengine
 
 namespace scheduler {
-class PhaseOffsets;
+class PhaseConfiguration;
+class RefreshRateConfigs;
 } // namespace scheduler
+
 namespace surfaceflinger {
 
 class NativeWindowSurface;
@@ -64,16 +68,17 @@
 // 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;
+    using SetVSyncEnabled = std::function<void(bool)>;
+
+    virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0;
+    virtual std::unique_ptr<EventControlThread> createEventControlThread(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,
-            const scheduler::RefreshRateConfigs& refreshRateConfig) = 0;
+    virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs&) = 0;
+    virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
+                                                       const scheduler::RefreshRateConfigs&,
+                                                       ISchedulerCallback&) = 0;
     virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
 
     virtual sp<StartPropertySetThread> createStartPropertySetThread(
@@ -85,6 +90,13 @@
     virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                                    sp<IGraphicBufferConsumer>* outConsumer,
                                    bool consumerIsSurfaceFlinger) = 0;
+    virtual sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>&,
+                                                               const sp<SurfaceFlinger>&,
+                                                               const wp<Layer>&) = 0;
+    virtual sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>&,
+                                                              renderengine::RenderEngine&,
+                                                              uint32_t tex, Layer*) = 0;
+
     virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>&) = 0;
 
@@ -92,11 +104,9 @@
 
     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<EffectLayer> createEffectLayer(const LayerCreationArgs& args) = 0;
     virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0;
 
-    virtual std::shared_ptr<TimeStats> createTimeStats() = 0;
-
 protected:
     ~Factory() = default;
 };
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 768074a..1a611f5 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -18,7 +18,9 @@
 #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
 #include <android/hardware/configstore/1.1/types.h>
 #include <configstore/Utils.h>
+#include <utils/Log.h>
 
+#include <log/log.h>
 #include <cstdlib>
 #include <tuple>
 
@@ -226,6 +228,18 @@
     return static_cast<int64_t>(defaultValue);
 }
 
+bool refresh_rate_switching(bool defaultValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    auto temp = SurfaceFlingerProperties::refresh_rate_switching();
+#pragma clang diagnostic pop
+    if (temp.has_value()) {
+        ALOGW("Using deprecated refresh_rate_switching sysprop. Value: %d", *temp);
+        return *temp;
+    }
+    return defaultValue;
+}
+
 int32_t set_idle_timer_ms(int32_t defaultValue) {
     auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
     if (temp.has_value()) {
@@ -250,8 +264,17 @@
     return defaultValue;
 }
 
-bool use_smart_90_for_video(bool defaultValue) {
-    auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
+bool use_content_detection_for_refresh_rate(bool defaultValue) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    auto smart_90_deprecated = SurfaceFlingerProperties::use_smart_90_for_video();
+#pragma clang diagnostic pop
+    if (smart_90_deprecated.has_value()) {
+        ALOGW("Using deprecated use_smart_90_for_video sysprop. Value: %d", *smart_90_deprecated);
+        return *smart_90_deprecated;
+    }
+
+    auto temp = SurfaceFlingerProperties::use_content_detection_for_refresh_rate();
     if (temp.has_value()) {
         return *temp;
     }
@@ -274,6 +297,14 @@
     return defaultValue;
 }
 
+bool use_frame_rate_api(bool defaultValue) {
+    auto temp = SurfaceFlingerProperties::use_frame_rate_api();
+    if (temp.has_value()) {
+        return *temp;
+    }
+    return defaultValue;
+}
+
 #define DISPLAY_PRIMARY_SIZE 3
 
 constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 5f88322..4c6e191 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -73,18 +73,22 @@
 int64_t color_space_agnostic_dataspace(
         android::hardware::graphics::common::V1_2::Dataspace defaultValue);
 
+bool refresh_rate_switching(bool defaultValue);
+
 int32_t set_idle_timer_ms(int32_t defaultValue);
 
 int32_t set_touch_timer_ms(int32_t defaultValue);
 
 int32_t set_display_power_timer_ms(int32_t defaultValue);
 
-bool use_smart_90_for_video(bool defaultValue);
+bool use_content_detection_for_refresh_rate(bool defaultValue);
 
 bool enable_protected_contents(bool defaultValue);
 
 bool support_kernel_idle_timer(bool defaultValue);
 
+bool use_frame_rate_api(bool defaultValue);
+
 android::ui::DisplayPrimaries getDisplayNativePrimaries();
 } // namespace sysprop
 } // namespace android
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 7bfe033..1f9d46c 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -13,6 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "SurfaceInterceptor"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -110,13 +114,22 @@
     addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack);
     addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy);
     addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius);
+    addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius);
     if (layer->mCurrentState.barrierLayer_legacy != nullptr) {
         addDeferTransactionLocked(transaction, layerId,
                                   layer->mCurrentState.barrierLayer_legacy.promote(),
                                   layer->mCurrentState.frameNumber_legacy);
     }
     addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode());
-    addFlagsLocked(transaction, layerId, layer->mCurrentState.flags);
+    addFlagsLocked(transaction, layerId, layer->mCurrentState.flags,
+                   layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
+                           layer_state_t::eLayerSecure);
+    addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mCurrentParent));
+    addDetachChildrenLocked(transaction, layerId, layer->isLayerDetached());
+    addRelativeParentLocked(transaction, layerId,
+                            getLayerIdFromWeakRef(layer->mCurrentState.zOrderRelativeOf),
+                            layer->mCurrentState.z);
+    addShadowRadiusLocked(transaction, layerId, layer->mCurrentState.shadowRadius);
 }
 
 void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
@@ -129,8 +142,8 @@
     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);
+    addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation),
+                               display.viewport, display.frame);
 }
 
 status_t SurfaceInterceptor::writeProtoFileLocked() {
@@ -150,7 +163,7 @@
     return NO_ERROR;
 }
 
-const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) {
+const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const {
     const sp<const IBinder>& handle(weakHandle.promote());
     const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
     const sp<const Layer> layer(layerHandle->owner.promote());
@@ -158,12 +171,25 @@
     return layer;
 }
 
-const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) {
-    return layer->getName().string();
+int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const {
+    return layer->sequence;
 }
 
-int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) {
-    return layer->sequence;
+int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const {
+    if (layer == nullptr) {
+        return -1;
+    }
+    auto strongLayer = layer.promote();
+    return strongLayer == nullptr ? -1 : getLayerId(strongLayer);
+}
+
+int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const {
+    if (handle == nullptr) {
+        return -1;
+    }
+    const auto layerHandle(static_cast<const Layer::Handle*>(handle.get()));
+    const sp<const Layer> layer(layerHandle->owner.promote());
+    return layer == nullptr ? -1 : getLayerId(layer);
 }
 
 Increment* SurfaceInterceptor::createTraceIncrementLocked() {
@@ -252,24 +278,23 @@
     }
 }
 
-void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId,
-        uint8_t flags)
-{
+void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags,
+                                        uint8_t mask) {
     // There can be multiple flags changed
-    if (flags & layer_state_t::eLayerHidden) {
+    if (mask & layer_state_t::eLayerHidden) {
         SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
         HiddenFlagChange* flagChange(change->mutable_hidden_flag());
-        flagChange->set_hidden_flag(true);
+        flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden);
     }
-    if (flags & layer_state_t::eLayerOpaque) {
+    if (mask & layer_state_t::eLayerOpaque) {
         SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
         OpaqueFlagChange* flagChange(change->mutable_opaque_flag());
-        flagChange->set_opaque_flag(true);
+        flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque);
     }
-    if (flags & layer_state_t::eLayerSecure) {
+    if (mask & layer_state_t::eLayerSecure) {
         SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
         SecureFlagChange* flagChange(change->mutable_secure_flag());
-        flagChange->set_secure_flag(true);
+        flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure);
     }
 }
 
@@ -298,6 +323,13 @@
     cornerRadiusChange->set_corner_radius(cornerRadius);
 }
 
+void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
+                                                       int32_t backgroundBlurRadius) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    BackgroundBlurRadiusChange* blurRadiusChange(change->mutable_background_blur_radius());
+    blurRadiusChange->set_background_blur_radius(backgroundBlurRadius);
+}
+
 void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
         const sp<const Layer>& layer, uint64_t frameNumber)
 {
@@ -320,6 +352,42 @@
     overrideChange->set_override_scaling_mode(overrideScalingMode);
 }
 
+void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId,
+                                           int32_t parentId) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    ReparentChange* overrideChange(change->mutable_reparent());
+    overrideChange->set_parent_id(parentId);
+}
+
+void SurfaceInterceptor::addReparentChildrenLocked(Transaction* transaction, int32_t layerId,
+                                                   int32_t parentId) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    ReparentChildrenChange* overrideChange(change->mutable_reparent_children());
+    overrideChange->set_parent_id(parentId);
+}
+
+void SurfaceInterceptor::addDetachChildrenLocked(Transaction* transaction, int32_t layerId,
+                                                 bool detached) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    DetachChildrenChange* overrideChange(change->mutable_detach_children());
+    overrideChange->set_detach_children(detached);
+}
+
+void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId,
+                                                 int32_t parentId, int z) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    RelativeParentChange* overrideChange(change->mutable_relative_parent());
+    overrideChange->set_relative_parent_id(parentId);
+    overrideChange->set_z(z);
+}
+
+void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
+                                               float shadowRadius) {
+    SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+    ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
+    overrideChange->set_radius(shadowRadius);
+}
+
 void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
         const layer_state_t& state)
 {
@@ -351,7 +419,7 @@
         addTransparentRegionLocked(transaction, layerId, state.transparentRegion);
     }
     if (state.what & layer_state_t::eFlagsChanged) {
-        addFlagsLocked(transaction, layerId, state.flags);
+        addFlagsLocked(transaction, layerId, state.flags, state.mask);
     }
     if (state.what & layer_state_t::eLayerStackChanged) {
         addLayerStackLocked(transaction, layerId, state.layerStack);
@@ -362,6 +430,9 @@
     if (state.what & layer_state_t::eCornerRadiusChanged) {
         addCornerRadiusLocked(transaction, layerId, state.cornerRadius);
     }
+    if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+        addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius);
+    }
     if (state.what & layer_state_t::eDeferTransaction_legacy) {
         sp<Layer> otherLayer = nullptr;
         if (state.barrierHandle_legacy != nullptr) {
@@ -380,6 +451,22 @@
     if (state.what & layer_state_t::eOverrideScalingModeChanged) {
         addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode);
     }
+    if (state.what & layer_state_t::eReparent) {
+        addReparentLocked(transaction, layerId, getLayerIdFromHandle(state.parentHandleForChild));
+    }
+    if (state.what & layer_state_t::eReparentChildren) {
+        addReparentChildrenLocked(transaction, layerId, getLayerIdFromHandle(state.reparentHandle));
+    }
+    if (state.what & layer_state_t::eDetachChildren) {
+        addDetachChildrenLocked(transaction, layerId, true);
+    }
+    if (state.what & layer_state_t::eRelativeLayerChanged) {
+        addRelativeParentLocked(transaction, layerId,
+                                getLayerIdFromHandle(state.relativeLayerHandle), state.z);
+    }
+    if (state.what & layer_state_t::eShadowRadiusChanged) {
+        addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
+    }
 }
 
 void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
@@ -395,8 +482,8 @@
         addDisplaySizeLocked(transaction, sequenceId, state.width, state.height);
     }
     if (state.what & DisplayState::eDisplayProjectionChanged) {
-        addDisplayProjectionLocked(transaction, sequenceId, state.orientation, state.viewport,
-                state.frame);
+        addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation),
+                                   state.viewport, state.frame);
     }
 }
 
@@ -425,7 +512,7 @@
 {
     SurfaceCreation* creation(increment->mutable_surface_creation());
     creation->set_id(getLayerId(layer));
-    creation->set_name(getLayerName(layer));
+    creation->set_name(layer->getName());
     creation->set_w(layer->mCurrentState.active_legacy.w);
     creation->set_h(layer->mCurrentState.active_legacy.h);
 }
@@ -508,8 +595,8 @@
     creation->set_id(info.sequenceId);
     creation->set_name(info.displayName);
     creation->set_is_secure(info.isSecure);
-    if (info.displayId) {
-        creation->set_display_id(info.displayId->value);
+    if (info.physical) {
+        creation->set_display_id(info.physical->id.value);
     }
 }
 
@@ -605,3 +692,6 @@
 
 } // namespace impl
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 563a44c..a665f62 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -39,8 +39,14 @@
 struct DisplayDeviceState;
 struct DisplayState;
 struct layer_state_t;
+using Transaction = surfaceflinger::Transaction;
+using Trace = surfaceflinger::Trace;
+using Rectangle = surfaceflinger::Rectangle;
+using SurfaceChange = surfaceflinger::SurfaceChange;
+using Increment = surfaceflinger::Increment;
+using DisplayChange = surfaceflinger::DisplayChange;
 
-constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
 
 class SurfaceInterceptor {
 public:
@@ -116,9 +122,10 @@
     void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display);
 
     status_t writeProtoFileLocked();
-    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle);
-    const std::string getLayerName(const sp<const Layer>& layer);
-    int32_t getLayerId(const sp<const Layer>& layer);
+    const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const;
+    int32_t getLayerId(const sp<const Layer>& layer) const;
+    int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const;
+    int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const;
 
     Increment* createTraceIncrementLocked();
     void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
@@ -141,10 +148,12 @@
             const layer_state_t::matrix22_t& matrix);
     void addTransparentRegionLocked(Transaction* transaction, int32_t layerId,
             const Region& transRegion);
-    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags);
+    void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask);
     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 addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId,
+                                       int32_t backgroundBlurRadius);
     void addDeferTransactionLocked(Transaction* transaction, int32_t layerId,
             const sp<const Layer>& layer, uint64_t frameNumber);
     void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId,
@@ -153,6 +162,12 @@
     void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates,
             const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays,
             const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags);
+    void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
+    void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId);
+    void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
+    void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
+                                 int z);
+    void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
 
     // Add display transactions to the trace
     DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
@@ -176,6 +191,7 @@
 };
 
 } // namespace impl
+
 } // namespace android
 
 #endif // ANDROID_SURFACEINTERCEPTOR_H
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index 5d9be0b..eb5c7de 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -13,6 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "SurfaceTracing"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -68,9 +72,8 @@
     return mEnabled;
 }
 
-void SurfaceTracing::notify(long compositionTime, const char* where) {
+void SurfaceTracing::notify(const char* where) {
     std::scoped_lock lock(mSfLock);
-    mCompositionTime = compositionTime;
     mWhere = where;
     mCanStartTrace.notify_one();
 }
@@ -161,9 +164,10 @@
     ATRACE_CALL();
 
     LayersTraceProto entry;
-    entry.set_elapsed_realtime_nanos(mCompositionTime);
+    entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
     entry.set_where(where);
     LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
+    mFlinger.dumpOffscreenLayersProto(layers);
     entry.mutable_layers()->Swap(&layers);
 
     return entry;
@@ -184,8 +188,11 @@
         ALOGE("Could not save the proto file! Permission denied");
         mLastErr = PERMISSION_DENIED;
     }
-    if (!android::base::WriteStringToFile(output, kDefaultFileName, S_IRWXU | S_IRGRP, getuid(),
-                                          getgid(), true)) {
+
+    // -rw-r--r--
+    const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+    if (!android::base::WriteStringToFile(output, kDefaultFileName, mode, getuid(), getgid(),
+                                          true)) {
         ALOGE("Could not save the proto file! There are missing fields");
         mLastErr = PERMISSION_DENIED;
     }
@@ -202,3 +209,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 395d562..18524f0 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -46,7 +46,7 @@
     bool disable();
     status_t writeToFile();
     bool isEnabled() const;
-    void notify(long compositionTime, const char* where);
+    void notify(const char* where);
 
     void setBufferSize(size_t bufferSizeInByte);
     void writeToFileAsync();
@@ -61,7 +61,7 @@
     void setTraceFlags(uint32_t flags);
 
 private:
-    static constexpr auto kDefaultBufferCapInByte = 100_MB;
+    static constexpr auto kDefaultBufferCapInByte = 5_MB;
     static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb";
 
     class LayersTraceBuffer { // ring buffer
@@ -81,8 +81,6 @@
         std::queue<LayersTraceProto> mStorage;
     };
 
-    long mCompositionTime;
-
     void mainLoop();
     void addFirstEntry();
     LayersTraceProto traceWhenNotified();
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
new file mode 100644
index 0000000..d27fbb4
--- /dev/null
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -0,0 +1,35 @@
+cc_library_shared {
+    name: "libtimestats",
+    srcs: [
+        "TimeStats.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libprotobuf-cpp-lite",
+        "libprotoutil",
+        "libstatslog",
+        "libstatspull",
+        "libstatssocket",
+        "libtimestats_proto",
+        "libui",
+        "libutils",
+    ],
+    export_include_dirs: ["."],
+    export_shared_lib_headers: [
+        "libprotoutil",
+        "libstatslog",
+        "libstatspull",
+        "libstatssocket",
+        "libtimestats_proto",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+}
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index c97a19b..8038eba 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -13,6 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 #undef LOG_TAG
 #define LOG_TAG "TimeStats"
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -20,20 +24,183 @@
 #include "TimeStats.h"
 
 #include <android-base/stringprintf.h>
-
+#include <android/util/ProtoOutputStream.h>
 #include <log/log.h>
-
 #include <utils/String8.h>
 #include <utils/Timers.h>
 #include <utils/Trace.h>
 
 #include <algorithm>
-#include <regex>
+#include <chrono>
 
 namespace android {
 
 namespace impl {
 
+AStatsManager_PullAtomCallbackReturn TimeStats::pullAtomCallback(int32_t atom_tag,
+                                                                 AStatsEventList* data,
+                                                                 void* cookie) {
+    impl::TimeStats* timeStats = reinterpret_cast<impl::TimeStats*>(cookie);
+    AStatsManager_PullAtomCallbackReturn result = AStatsManager_PULL_SKIP;
+    if (atom_tag == android::util::SURFACEFLINGER_STATS_GLOBAL_INFO) {
+        result = timeStats->populateGlobalAtom(data);
+    } else if (atom_tag == android::util::SURFACEFLINGER_STATS_LAYER_INFO) {
+        result = timeStats->populateLayerAtom(data);
+    }
+
+    // Enable timestats now. The first full pull for a given build is expected to
+    // have empty or very little stats, as stats are first enabled after the
+    // first pull is completed for either the global or layer stats.
+    timeStats->enable();
+    return result;
+}
+
+namespace {
+// Histograms align with the order of fields in SurfaceflingerStatsLayerInfo.
+const std::array<std::string, 6> kHistogramNames = {
+        "present2present", "post2present",    "acquire2present",
+        "latch2present",   "desired2present", "post2acquire",
+};
+
+std::string histogramToProtoByteString(const std::unordered_map<int32_t, int32_t>& histogram,
+                                       size_t maxPulledHistogramBuckets) {
+    auto buckets = std::vector<std::pair<int32_t, int32_t>>(histogram.begin(), histogram.end());
+    std::sort(buckets.begin(), buckets.end(),
+              [](std::pair<int32_t, int32_t>& left, std::pair<int32_t, int32_t>& right) {
+                  return left.second > right.second;
+              });
+
+    util::ProtoOutputStream proto;
+    int histogramSize = 0;
+    for (const auto& bucket : buckets) {
+        if (++histogramSize > maxPulledHistogramBuckets) {
+            break;
+        }
+        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+                            1 /* field id */,
+                    (int32_t)bucket.first);
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            2 /* field id */,
+                    (int64_t)bucket.second);
+    }
+
+    std::string byteString;
+    proto.serializeToString(&byteString);
+    return byteString;
+}
+} // namespace
+
+AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    if (mTimeStats.statsStart == 0) {
+        return AStatsManager_PULL_SKIP;
+    }
+    flushPowerTimeLocked();
+
+    AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
+    mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime);
+    mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime());
+    mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCount);
+    std::string frameDurationBytes =
+            histogramToProtoByteString(mTimeStats.frameDuration.hist, mMaxPulledHistogramBuckets);
+    mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(),
+                                             frameDurationBytes.size());
+    std::string renderEngineTimingBytes =
+            histogramToProtoByteString(mTimeStats.renderEngineTiming.hist,
+                                       mMaxPulledHistogramBuckets);
+    mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(),
+                                             renderEngineTimingBytes.size());
+    mStatsDelegate->statsEventBuild(event);
+    clearGlobalLocked();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) {
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats;
+    for (const auto& ele : mTimeStats.stats) {
+        dumpStats.push_back(&ele.second);
+    }
+
+    std::sort(dumpStats.begin(), dumpStats.end(),
+              [](TimeStatsHelper::TimeStatsLayer const* l,
+                 TimeStatsHelper::TimeStatsLayer const* r) {
+                  return l->totalFrames > r->totalFrames;
+              });
+
+    if (mMaxPulledLayers < dumpStats.size()) {
+        dumpStats.resize(mMaxPulledLayers);
+    }
+
+    for (const auto& layer : dumpStats) {
+        AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data);
+        mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+        mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str());
+        mStatsDelegate->statsEventWriteInt64(event, layer->totalFrames);
+        mStatsDelegate->statsEventWriteInt64(event, layer->droppedFrames);
+
+        for (const auto& name : kHistogramNames) {
+            const auto& histogram = layer->deltas.find(name);
+            if (histogram == layer->deltas.cend()) {
+                mStatsDelegate->statsEventWriteByteArray(event, nullptr, 0);
+            } else {
+                std::string bytes = histogramToProtoByteString(histogram->second.hist,
+                                                               mMaxPulledHistogramBuckets);
+                mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)bytes.c_str(),
+                                                         bytes.size());
+            }
+        }
+
+        mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames);
+        mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames);
+
+        mStatsDelegate->statsEventBuild(event);
+    }
+    clearLayersLocked();
+
+    return AStatsManager_PULL_SUCCESS;
+}
+
+TimeStats::TimeStats() : TimeStats(nullptr, std::nullopt, std::nullopt) {}
+
+TimeStats::TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
+                     std::optional<size_t> maxPulledLayers,
+                     std::optional<size_t> maxPulledHistogramBuckets) {
+    if (statsDelegate != nullptr) {
+        mStatsDelegate = std::move(statsDelegate);
+    }
+
+    if (maxPulledLayers) {
+        mMaxPulledLayers = *maxPulledLayers;
+    }
+
+    if (maxPulledHistogramBuckets) {
+        mMaxPulledHistogramBuckets = *maxPulledHistogramBuckets;
+    }
+}
+
+TimeStats::~TimeStats() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mStatsDelegate->unregisterStatsPullAtomCallback(
+            android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+    mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+}
+
+void TimeStats::onBootFinished() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                                  TimeStats::pullAtomCallback, nullptr, this);
+    mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                                  TimeStats::pullAtomCallback, nullptr, this);
+}
+
 void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
     ATRACE_CALL();
 
@@ -59,7 +226,7 @@
     }
 
     if (argsMap.count("-clear")) {
-        clear();
+        clearAll();
     }
 
     if (argsMap.count("-enable")) {
@@ -72,8 +239,10 @@
 
     std::string result = "TimeStats miniDump:\n";
     std::lock_guard<std::mutex> lock(mMutex);
-    android::base::StringAppendF(&result, "Number of tracked layers is %zu\n",
+    android::base::StringAppendF(&result, "Number of layers currently being tracked is %zu\n",
                                  mTimeStatsTracker.size());
+    android::base::StringAppendF(&result, "Number of layers in the stats pool is %zu\n",
+                                 mTimeStats.stats.size());
     return result;
 }
 
@@ -104,9 +273,68 @@
     mTimeStats.clientCompositionFrames++;
 }
 
-bool TimeStats::recordReadyLocked(int32_t layerID, TimeRecord* timeRecord) {
+void TimeStats::incrementClientCompositionReusedFrames() {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.clientCompositionReusedFrames++;
+}
+
+void TimeStats::recordDisplayEventConnectionCount(int32_t count) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStats.displayEventConnectionsCount =
+            std::max(mTimeStats.displayEventConnectionsCount, count);
+}
+
+static int32_t msBetween(nsecs_t start, nsecs_t end) {
+    int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+                            std::chrono::nanoseconds(end - start))
+                            .count();
+    delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
+    return static_cast<int32_t>(delta);
+}
+
+void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mPowerTime.powerMode == HWC_POWER_MODE_NORMAL) {
+        mTimeStats.frameDuration.insert(msBetween(startTime, endTime));
+    }
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+        ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
+    mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime,
+                                           const std::shared_ptr<FenceTime>& endTime) {
+    if (!mEnabled.load()) return;
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+        ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
+    mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
+bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
     if (!timeRecord->ready) {
-        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerID,
+        ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
               timeRecord->frameTime.frameNumber);
         return false;
     }
@@ -119,7 +347,7 @@
             timeRecord->frameTime.acquireTime = timeRecord->acquireFence->getSignalTime();
             timeRecord->acquireFence = nullptr;
         } else {
-            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-acquireFence signal time is invalid", layerId,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -132,7 +360,7 @@
             timeRecord->frameTime.presentTime = timeRecord->presentFence->getSignalTime();
             timeRecord->presentFence = nullptr;
         } else {
-            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-presentFence signal time invalid", layerId,
                   timeRecord->frameTime.frameNumber);
         }
     }
@@ -140,138 +368,105 @@
     return true;
 }
 
-static int32_t msBetween(nsecs_t start, nsecs_t end) {
-    int64_t delta = (end - start) / 1000000;
-    delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
-    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) {
-    std::smatch match;
-    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];
-    }
-    return "";
-}
-
-void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) {
+void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
     ATRACE_CALL();
 
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
     std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
     while (!timeRecords.empty()) {
-        if (!recordReadyLocked(layerID, &timeRecords[0])) break;
-        ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID,
+        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) {
+            const std::string& layerName = layerRecord.layerName;
             if (!mTimeStats.stats.count(layerName)) {
                 mTimeStats.stats[layerName].layerName = layerName;
-                mTimeStats.stats[layerName].packageName = getPackageName(layerName);
             }
             TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName];
             timeStatsLayer.totalFrames++;
             timeStatsLayer.droppedFrames += layerRecord.droppedFrames;
+            timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames;
+            timeStatsLayer.badDesiredPresentFrames += layerRecord.badDesiredPresentFrames;
+
             layerRecord.droppedFrames = 0;
+            layerRecord.lateAcquireFrames = 0;
+            layerRecord.badDesiredPresentFrames = 0;
 
             const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime,
                                                       timeRecords[0].frameTime.acquireTime);
-            ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID,
+            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,
+            ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, postToPresentMs);
             timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
 
             const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, acquireToPresentMs);
             timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
 
             const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime,
                                                        timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, latchToPresentMs);
             timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
 
             const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, desiredToPresentMs);
             timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
 
             const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime,
                                                          timeRecords[0].frameTime.presentTime);
-            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID,
+            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
                   timeRecords[0].frameTime.frameNumber, presentToPresentMs);
             timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
         }
-
-        // 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 constexpr const char* kPopupWindowPrefix = "PopupWindow";
+static const size_t kMinLenLayerName = std::strlen(kPopupWindowPrefix);
 
+// Avoid tracking the "PopupWindow:<random hash>#<number>" layers
 static bool layerNameIsValid(const std::string& layerName) {
-    return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex);
+    return layerName.length() >= kMinLenLayerName &&
+            layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0;
 }
 
-void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                             nsecs_t postTime) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(),
+    ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerId, frameNumber, layerName.c_str(),
           postTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
-        layerNameIsValid(layerName)) {
-        mTimeStatsTracker[layerID].layerName = layerName;
+    if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) {
+        return;
     }
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS &&
+        layerNameIsValid(layerName)) {
+        mTimeStatsTracker[layerId].layerName = layerName;
+    }
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
         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);
+              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
@@ -293,15 +488,15 @@
         layerRecord.waitData = layerRecord.timeRecords.size() - 1;
 }
 
-void TimeStats::setLatchTime(int32_t layerID, 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("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime);
+    ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerId, frameNumber, latchTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -311,15 +506,45 @@
     }
 }
 
-void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) {
+void TimeStats::incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime);
+    ALOGV("[%d]-LatchSkipped-Reason[%d]", layerId,
+          static_cast<std::underlying_type<LatchSkipReason>::type>(reason));
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+
+    switch (reason) {
+        case LatchSkipReason::LateAcquire:
+            layerRecord.lateAcquireFrames++;
+            break;
+    }
+}
+
+void TimeStats::incrementBadDesiredPresent(int32_t layerId) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-BadDesiredPresent", layerId);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
+    layerRecord.badDesiredPresentFrames++;
+}
+
+void TimeStats::setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) {
+    if (!mEnabled.load()) return;
+
+    ATRACE_CALL();
+    ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerId, frameNumber, desiredTime);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -329,15 +554,15 @@
     }
 }
 
-void TimeStats::setAcquireTime(int32_t layerID, 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("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime);
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerId, frameNumber, acquireTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -347,17 +572,17 @@
     }
 }
 
-void TimeStats::setAcquireFence(int32_t layerID, 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("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerId, frameNumber,
           acquireFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -367,15 +592,15 @@
     }
 }
 
-void TimeStats::setPresentTime(int32_t layerID, 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("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime);
+    ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerId, frameNumber, presentTime);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -386,20 +611,20 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerID);
+    flushAvailableRecordsToStatsLocked(layerId);
 }
 
-void TimeStats::setPresentFence(int32_t layerID, 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("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber,
+    ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerId, frameNumber,
           presentFence->getSignalTime());
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     if (layerRecord.waitData < 0 ||
         layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
         return;
@@ -410,29 +635,25 @@
         layerRecord.waitData++;
     }
 
-    flushAvailableRecordsToStatsLocked(layerID);
+    flushAvailableRecordsToStatsLocked(layerId);
 }
 
-void TimeStats::onDestroy(int32_t layerID) {
+void TimeStats::onDestroy(int32_t layerId) {
+    ATRACE_CALL();
+    ALOGV("[%d]-onDestroy", layerId);
+    std::lock_guard<std::mutex> lock(mMutex);
+    mTimeStatsTracker.erase(layerId);
+}
+
+void TimeStats::removeTimeRecord(int32_t layerId, uint64_t frameNumber) {
     if (!mEnabled.load()) return;
 
     ATRACE_CALL();
-    ALOGV("[%d]-onDestroy", layerID);
+    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerId, frameNumber);
 
     std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    mTimeStatsTracker.erase(layerID);
-}
-
-void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) {
-    if (!mEnabled.load()) return;
-
-    ATRACE_CALL();
-    ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber);
-
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (!mTimeStatsTracker.count(layerID)) return;
-    LayerRecord& layerRecord = mTimeStatsTracker[layerID];
+    if (!mTimeStatsTracker.count(layerId)) return;
+    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
     size_t removeAt = 0;
     for (const TimeRecord& record : layerRecord.timeRecords) {
         if (record.frameTime.frameNumber == frameNumber) break;
@@ -518,6 +739,31 @@
         mGlobalRecord.prevPresentTime = curPresentTime;
         mGlobalRecord.presentFences.pop_front();
     }
+    while (!mGlobalRecord.renderEngineDurations.empty()) {
+        const auto duration = mGlobalRecord.renderEngineDurations.front();
+        const auto& endTime = duration.endTime;
+
+        nsecs_t endNs = -1;
+
+        if (auto val = std::get_if<nsecs_t>(&endTime)) {
+            endNs = *val;
+        } else {
+            endNs = std::get<std::shared_ptr<FenceTime>>(endTime)->getSignalTime();
+        }
+
+        if (endNs == Fence::SIGNAL_TIME_PENDING) break;
+
+        if (endNs < 0) {
+            ALOGE("RenderEngineTiming is invalid!");
+            mGlobalRecord.renderEngineDurations.pop_front();
+            continue;
+        }
+
+        const int32_t renderEngineMs = msBetween(duration.startTime, endNs);
+        mTimeStats.renderEngineTiming.insert(renderEngineMs);
+
+        mGlobalRecord.renderEngineDurations.pop_front();
+    }
 }
 
 void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
@@ -574,24 +820,39 @@
     ALOGD("Disabled");
 }
 
-void TimeStats::clear() {
+void TimeStats::clearAll() {
+    std::lock_guard<std::mutex> lock(mMutex);
+    clearGlobalLocked();
+    clearLayersLocked();
+}
+
+void TimeStats::clearGlobalLocked() {
     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.clientCompositionReusedFrames = 0;
+    mTimeStats.displayEventConnectionsCount = 0;
     mTimeStats.displayOnTime = 0;
     mTimeStats.presentToPresent.hist.clear();
+    mTimeStats.frameDuration.hist.clear();
+    mTimeStats.renderEngineTiming.hist.clear();
     mTimeStats.refreshRateStats.clear();
     mPowerTime.prevTime = systemTime();
     mGlobalRecord.prevPresentTime = 0;
     mGlobalRecord.presentFences.clear();
-    ALOGD("Cleared");
+    ALOGD("Cleared global stats");
+}
+
+void TimeStats::clearLayersLocked() {
+    ATRACE_CALL();
+
+    mTimeStatsTracker.clear();
+    mTimeStats.stats.clear();
+    ALOGD("Cleared layer stats");
 }
 
 bool TimeStats::isEnabled() {
@@ -613,7 +874,7 @@
     if (asProto) {
         ALOGD("Dumping TimeStats as proto");
         SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers);
-        result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
+        result.append(timeStatsProto.SerializeAsString());
     } else {
         ALOGD("Dumping TimeStats as text");
         result.append(mTimeStats.toString(maxLayers));
@@ -624,3 +885,6 @@
 } // namespace impl
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 2bcb568..ddebeb1 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -16,13 +16,13 @@
 
 #pragma once
 
+#include <hardware/hwcomposer_defs.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
 #include <timestatsproto/TimeStatsHelper.h>
 #include <timestatsproto/TimeStatsProtoHeader.h>
-
-#include <hardware/hwcomposer_defs.h>
-
 #include <ui/FenceTime.h>
-
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
@@ -30,6 +30,7 @@
 #include <mutex>
 #include <optional>
 #include <unordered_map>
+#include <variant>
 
 using namespace android::surfaceflinger;
 
@@ -39,6 +40,10 @@
 public:
     virtual ~TimeStats() = default;
 
+    // Called once boot has been finished to perform additional capabilities,
+    // e.g. registration to statsd.
+    virtual void onBootFinished() = 0;
+
     virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0;
     virtual bool isEnabled() = 0;
     virtual std::string miniDump() = 0;
@@ -46,21 +51,53 @@
     virtual void incrementTotalFrames() = 0;
     virtual void incrementMissedFrames() = 0;
     virtual void incrementClientCompositionFrames() = 0;
+    virtual void incrementClientCompositionReusedFrames() = 0;
+    // Records the most up-to-date count of display event connections.
+    // The stored count will be the maximum ever recoded.
+    virtual void recordDisplayEventConnectionCount(int32_t count) = 0;
 
-    virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+    // Records the start and end times for a frame.
+    // The start time is the same as the beginning of a SurfaceFlinger
+    // invalidate message.
+    // The end time corresponds to when SurfaceFlinger finishes submitting the
+    // request to HWC to present a frame.
+    virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+    // Records the start time and end times for when RenderEngine begins work.
+    // The start time corresponds to the beginning of RenderEngine::drawLayers.
+    // The end time corresponds to when RenderEngine finishes rendering.
+    virtual void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+    // Same as above, but passes in a fence representing the end time.
+    virtual void recordRenderEngineDuration(nsecs_t startTime,
+                                            const std::shared_ptr<FenceTime>& readyFence) = 0;
+
+    virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                              nsecs_t postTime) = 0;
-    virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0;
-    virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0;
-    virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0;
-    virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+    virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
+    // Reasons why latching a particular buffer may be skipped
+    enum class LatchSkipReason {
+        // If the acquire fence did not fire on some devices we skip latching
+        // the buffer until the fence fires.
+        LateAcquire,
+    };
+    // Increments the counter of skipped latch buffers.
+    virtual void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) = 0;
+    // Increments the counter of bad desired present times for this layer.
+    // Bad desired present times are "implausible" and cause SurfaceFlinger to
+    // latch a buffer immediately to avoid stalling.
+    virtual void incrementBadDesiredPresent(int32_t layerId) = 0;
+    virtual void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) = 0;
+    virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
+    virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& acquireFence) = 0;
-    virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0;
-    virtual void setPresentFence(int32_t layerID, uint64_t frameNumber,
+    // SetPresent{Time, Fence} are not expected to be called in the critical
+    // rendering path, as they flush prior fences if those fences have fired.
+    virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
+    virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
                                  const std::shared_ptr<FenceTime>& presentFence) = 0;
     // Clean up the layer record
-    virtual void onDestroy(int32_t layerID) = 0;
+    virtual void onDestroy(int32_t layerId) = 0;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0;
+    virtual void removeTimeRecord(int32_t layerId, uint64_t frameNumber) = 0;
 
     virtual void setPowerMode(int32_t powerMode) = 0;
     // Source of truth is RefrehRateStats.
@@ -94,6 +131,8 @@
         // fences to signal, but rather waiting to receive those fences/timestamps.
         int32_t waitData = -1;
         uint32_t droppedFrames = 0;
+        uint32_t lateAcquireFrames = 0;
+        uint32_t badDesiredPresentFrames = 0;
         TimeRecord prevTimeRecord;
         std::deque<TimeRecord> timeRecords;
     };
@@ -103,14 +142,71 @@
         nsecs_t prevTime = 0;
     };
 
+    struct RenderEngineDuration {
+        nsecs_t startTime;
+        std::variant<nsecs_t, std::shared_ptr<FenceTime>> endTime;
+    };
+
     struct GlobalRecord {
         nsecs_t prevPresentTime = 0;
         std::deque<std::shared_ptr<FenceTime>> presentFences;
+        std::deque<RenderEngineDuration> renderEngineDurations;
     };
 
 public:
-    TimeStats() = default;
+    TimeStats();
 
+    // Delegate to the statsd service and associated APIs.
+    // Production code may use this class directly, whereas unit test may define
+    // a subclass for ease of testing.
+    class StatsEventDelegate {
+    public:
+        virtual ~StatsEventDelegate() = default;
+        virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
+            return AStatsEventList_addStatsEvent(data);
+        }
+        virtual void registerStatsPullAtomCallback(int32_t atom_tag,
+                                                   AStatsManager_PullAtomCallback callback,
+                                                   AStatsManager_PullAtomMetadata* metadata,
+                                                   void* cookie) {
+            return AStatsManager_registerPullAtomCallback(atom_tag, callback, metadata, cookie);
+        }
+
+        virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
+            return AStatsManager_unregisterPullAtomCallback(atom_tag);
+        }
+
+        virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
+            return AStatsEvent_setAtomId(event, atom_id);
+        }
+
+        virtual void statsEventWriteInt32(AStatsEvent* event, int32_t field) {
+            return AStatsEvent_writeInt32(event, field);
+        }
+
+        virtual void statsEventWriteInt64(AStatsEvent* event, int64_t field) {
+            return AStatsEvent_writeInt64(event, field);
+        }
+
+        virtual void statsEventWriteString8(AStatsEvent* event, const char* field) {
+            return AStatsEvent_writeString(event, field);
+        }
+
+        virtual void statsEventWriteByteArray(AStatsEvent* event, const uint8_t* buf,
+                                              size_t numBytes) {
+            return AStatsEvent_writeByteArray(event, buf, numBytes);
+        }
+
+        virtual void statsEventBuild(AStatsEvent* event) { return AStatsEvent_build(event); }
+    };
+    // For testing only for injecting custom dependencies.
+    TimeStats(std::unique_ptr<StatsEventDelegate> statsDelegate,
+              std::optional<size_t> maxPulledLayers,
+              std::optional<size_t> maxPulledHistogramBuckets);
+
+    ~TimeStats() override;
+
+    void onBootFinished() override;
     void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override;
     bool isEnabled() override;
     std::string miniDump() override;
@@ -118,21 +214,30 @@
     void incrementTotalFrames() override;
     void incrementMissedFrames() override;
     void incrementClientCompositionFrames() override;
+    void incrementClientCompositionReusedFrames() override;
+    void recordDisplayEventConnectionCount(int32_t count) override;
 
-    void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName,
+    void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
+    void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
+    void recordRenderEngineDuration(nsecs_t startTime,
+                                    const std::shared_ptr<FenceTime>& readyFence) override;
+
+    void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
                      nsecs_t postTime) override;
-    void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override;
-    void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override;
-    void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override;
-    void setAcquireFence(int32_t layerID, uint64_t frameNumber,
+    void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
+    void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
+    void incrementBadDesiredPresent(int32_t layerId) override;
+    void setDesiredTime(int32_t layerId, uint64_t frameNumber, nsecs_t desiredTime) override;
+    void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override;
+    void setAcquireFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& acquireFence) override;
-    void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override;
-    void setPresentFence(int32_t layerID, uint64_t frameNumber,
+    void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override;
+    void setPresentFence(int32_t layerId, uint64_t frameNumber,
                          const std::shared_ptr<FenceTime>& presentFence) override;
     // Clean up the layer record
-    void onDestroy(int32_t layerID) override;
+    void onDestroy(int32_t layerId) override;
     // If SF skips or rejects a buffer, remove the corresponding TimeRecord.
-    void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override;
+    void removeTimeRecord(int32_t layerId, uint64_t frameNumber) override;
 
     void setPowerMode(int32_t powerMode) override;
     // Source of truth is RefrehRateStats.
@@ -142,25 +247,36 @@
     static const size_t MAX_NUM_TIME_RECORDS = 64;
 
 private:
-    bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord);
-    void flushAvailableRecordsToStatsLocked(int32_t layerID);
+    static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atom_tag,
+                                                                 AStatsEventList* data,
+                                                                 void* cookie);
+    AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data);
+    AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data);
+    bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
+    void flushAvailableRecordsToStatsLocked(int32_t layerId);
     void flushPowerTimeLocked();
     void flushAvailableGlobalRecordsToStatsLocked();
 
     void enable();
     void disable();
-    void clear();
+    void clearAll();
+    void clearGlobalLocked();
+    void clearLayersLocked();
     void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result);
 
     std::atomic<bool> mEnabled = false;
     std::mutex mMutex;
     TimeStatsHelper::TimeStatsGlobal mTimeStats;
-    // Hashmap for LayerRecord with layerID as the hash key
+    // Hashmap for LayerRecord with layerId as the hash key
     std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker;
     PowerTime mPowerTime;
     GlobalRecord mGlobalRecord;
 
     static const size_t MAX_NUM_LAYER_RECORDS = 200;
+    static const size_t MAX_NUM_LAYER_STATS = 200;
+    std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>();
+    size_t mMaxPulledLayers = 8;
+    size_t mMaxPulledHistogramBuckets = 6;
 };
 
 } // namespace impl
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 16d2da0..e2f85cc 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -83,6 +83,8 @@
     StringAppendF(&result, "packageName = %s\n", packageName.c_str());
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
+    StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
+    StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames);
     const auto iter = deltas.find("present2present");
     if (iter != deltas.end()) {
         StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime());
@@ -102,6 +104,7 @@
     StringAppendF(&result, "totalFrames = %d\n", totalFrames);
     StringAppendF(&result, "missedFrames = %d\n", missedFrames);
     StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames);
+    StringAppendF(&result, "clientCompositionReusedFrames = %d\n", clientCompositionReusedFrames);
     StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime);
     StringAppendF(&result, "displayConfigStats is as below:\n");
     for (const auto& [fps, duration] : refreshRateStats) {
@@ -111,6 +114,16 @@
     StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
     StringAppendF(&result, "presentToPresent histogram is as below:\n");
     result.append(presentToPresent.toString());
+    const float averageFrameDuration = frameDuration.averageTime();
+    StringAppendF(&result, "averageFrameDuration = %.3f ms\n",
+                  std::isnan(averageFrameDuration) ? 0.0f : averageFrameDuration);
+    StringAppendF(&result, "frameDuration histogram is as below:\n");
+    result.append(frameDuration.toString());
+    const float averageRenderEngineTiming = renderEngineTiming.averageTime();
+    StringAppendF(&result, "averageRenderEngineTiming = %.3f ms\n",
+                  std::isnan(averageRenderEngineTiming) ? 0.0f : averageRenderEngineTiming);
+    StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
+    result.append(renderEngineTiming.toString());
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         result.append(ele->toString());
@@ -158,6 +171,16 @@
         histProto->set_time_millis(histEle.first);
         histProto->set_frame_count(histEle.second);
     }
+    for (const auto& histEle : frameDuration.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_frame_duration();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
+    for (const auto& histEle : renderEngineTiming.hist) {
+        SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing();
+        histProto->set_time_millis(histEle.first);
+        histProto->set_frame_count(histEle.second);
+    }
     const auto dumpStats = generateDumpStats(maxLayers);
     for (const auto& ele : dumpStats) {
         SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index f2ac7ff..5e7c449 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -46,6 +46,8 @@
         std::string packageName;
         int32_t totalFrames = 0;
         int32_t droppedFrames = 0;
+        int32_t lateAcquireFrames = 0;
+        int32_t badDesiredPresentFrames = 0;
         std::unordered_map<std::string, Histogram> deltas;
 
         std::string toString() const;
@@ -59,8 +61,12 @@
         int32_t totalFrames = 0;
         int32_t missedFrames = 0;
         int32_t clientCompositionFrames = 0;
+        int32_t clientCompositionReusedFrames = 0;
+        int32_t displayEventConnectionsCount = 0;
         int64_t displayOnTime = 0;
         Histogram presentToPresent;
+        Histogram frameDuration;
+        Histogram renderEngineTiming;
         std::unordered_map<std::string, TimeStatsLayer> stats;
         std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
 
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 0dacbeb..5fd4a39 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
 // changes to these messages, and keep google3 side proto messages in sync if
 // the end to end pipeline needs to be updated.
 
-// Next tag: 10
+// Next tag: 12
 message SFTimeStatsGlobalProto {
   // The stats start time in UTC as seconds since January 1, 1970
   optional int64 stats_start = 1;
@@ -43,6 +43,10 @@
   repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9;
   // Present to present histogram.
   repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
+  // Frame CPU duration histogram.
+  repeated SFTimeStatsHistogramBucketProto frame_duration = 10;
+  // Frame GPU duration histogram.
+  repeated SFTimeStatsHistogramBucketProto render_engine_timing = 11;
   // Stats per layer. Apps could have multiple layers.
   repeated SFTimeStatsLayerProto stats = 6;
 }
diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h
new file mode 100644
index 0000000..c145a39
--- /dev/null
+++ b/services/surfaceflinger/TracedOrdinal.h
@@ -0,0 +1,64 @@
+/*
+ * 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/stringprintf.h>
+#include <utils/Trace.h>
+#include <cmath>
+#include <string>
+
+template <typename T>
+class TracedOrdinal {
+public:
+    static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()),
+                  "Type is not supported. Please test it with systrace before adding "
+                  "it to the list.");
+
+    TracedOrdinal(const std::string& name, T initialValue)
+          : mName(name),
+            mNameNegative(android::base::StringPrintf("%sNegative", name.c_str())),
+            mHasGoneNegative(std::signbit(initialValue)),
+            mData(initialValue) {
+        trace();
+    }
+
+    operator T() const { return mData; }
+
+    TracedOrdinal& operator=(T other) {
+        mData = other;
+        mHasGoneNegative = mHasGoneNegative || std::signbit(mData);
+        trace();
+        return *this;
+    }
+
+private:
+    void trace() {
+        if (!std::signbit(mData)) {
+            ATRACE_INT64(mName.c_str(), int64_t(mData));
+            if (mHasGoneNegative) {
+                ATRACE_INT64(mNameNegative.c_str(), 0);
+            }
+        } else {
+            ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData));
+            ATRACE_INT64(mName.c_str(), 0);
+        }
+    }
+
+    const std::string mName;
+    const std::string mNameNegative;
+    bool mHasGoneNegative;
+    T mData;
+};
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index fd466de..0cdff8f 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "TransactionCompletedThread"
@@ -24,7 +28,6 @@
 #include <cinttypes>
 
 #include <binder/IInterface.h>
-#include <gui/ITransactionCompletedListener.h>
 #include <utils/RefBase.h>
 
 namespace android {
@@ -58,7 +61,7 @@
     {
         std::lock_guard lock(mMutex);
         for (const auto& [listener, transactionStats] : mCompletedTransactions) {
-            IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
+            listener->unlinkToDeath(mDeathRecipient);
         }
     }
 }
@@ -75,27 +78,59 @@
     mThread = std::thread(&TransactionCompletedThread::threadMain, this);
 }
 
-status_t TransactionCompletedThread::addCallback(const sp<ITransactionCompletedListener>& listener,
-                                                 const std::vector<CallbackId>& callbackIds) {
+status_t TransactionCompletedThread::startRegistration(const ListenerCallbacks& listenerCallbacks) {
+    // begin running if not already running
+    run();
     std::lock_guard lock(mMutex);
     if (!mRunning) {
         ALOGE("cannot add callback because the callback thread isn't running");
         return BAD_VALUE;
     }
 
-    if (mCompletedTransactions.count(listener) == 0) {
-        status_t err = IInterface::asBinder(listener)->linkToDeath(mDeathRecipient);
-        if (err != NO_ERROR) {
-            ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
-            return err;
+    auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks);
+    auto& [listener, callbackIds] = listenerCallbacks;
+
+    if (inserted) {
+        if (mCompletedTransactions.count(listener) == 0) {
+            status_t err = listener->linkToDeath(mDeathRecipient);
+            if (err != NO_ERROR) {
+                ALOGE("cannot add callback because linkToDeath failed, err: %d", err);
+                return err;
+            }
         }
+        auto& transactionStatsDeque = mCompletedTransactions[listener];
+        transactionStatsDeque.emplace_back(callbackIds);
     }
 
-    auto& transactionStatsDeque = mCompletedTransactions[listener];
-    transactionStatsDeque.emplace_back(callbackIds);
     return NO_ERROR;
 }
 
+status_t TransactionCompletedThread::endRegistration(const ListenerCallbacks& listenerCallbacks) {
+    std::lock_guard lock(mMutex);
+    if (!mRunning) {
+        ALOGE("cannot add callback because the callback thread isn't running");
+        return BAD_VALUE;
+    }
+
+    auto itr = mRegisteringTransactions.find(listenerCallbacks);
+    if (itr == mRegisteringTransactions.end()) {
+        ALOGE("cannot end a registration that does not exist");
+        return BAD_VALUE;
+    }
+
+    mRegisteringTransactions.erase(itr);
+
+    return NO_ERROR;
+}
+
+bool TransactionCompletedThread::isRegisteringTransaction(
+        const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) {
+    ListenerCallbacks listenerCallbacks(transactionListener, callbackIds);
+
+    auto itr = mRegisteringTransactions.find(listenerCallbacks);
+    return itr != mRegisteringTransactions.end();
+}
+
 status_t TransactionCompletedThread::registerPendingCallbackHandle(
         const sp<CallbackHandle>& handle) {
     std::lock_guard lock(mMutex);
@@ -105,7 +140,7 @@
     }
 
     // If we can't find the transaction stats something has gone wrong. The client should call
-    // addCallback before trying to register a pending callback handle.
+    // startRegistration before trying to register a pending callback handle.
     TransactionStats* transactionStats;
     status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
     if (err != NO_ERROR) {
@@ -117,7 +152,7 @@
     return NO_ERROR;
 }
 
-status_t TransactionCompletedThread::addPresentedCallbackHandles(
+status_t TransactionCompletedThread::finalizePendingCallbackHandles(
         const std::deque<sp<CallbackHandle>>& handles) {
     std::lock_guard lock(mMutex);
     if (!mRunning) {
@@ -158,7 +193,7 @@
     return NO_ERROR;
 }
 
-status_t TransactionCompletedThread::addUnpresentedCallbackHandle(
+status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
         const sp<CallbackHandle>& handle) {
     std::lock_guard lock(mMutex);
     if (!mRunning) {
@@ -170,8 +205,8 @@
 }
 
 status_t TransactionCompletedThread::findTransactionStats(
-        const sp<ITransactionCompletedListener>& listener,
-        const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) {
+        const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds,
+        TransactionStats** outTransactionStats) {
     auto& transactionStatsDeque = mCompletedTransactions[listener];
 
     // Search back to front because the most recent transactions are at the back of the deque
@@ -189,7 +224,7 @@
 
 status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) {
     // If we can't find the transaction stats something has gone wrong. The client should call
-    // addCallback before trying to add a presnted callback handle.
+    // startRegistration before trying to add a callback handle.
     TransactionStats* transactionStats;
     status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats);
     if (err != NO_ERROR) {
@@ -202,8 +237,13 @@
     // destroyed the client side is dead and there won't be anyone to send the callback to.
     sp<IBinder> surfaceControl = handle->surfaceControl.promote();
     if (surfaceControl) {
+        FrameEventHistoryStats eventStats(handle->frameNumber,
+                                          handle->gpuCompositionDoneFence->getSnapshot().fence,
+                                          handle->compositorTiming, handle->refreshStartTime,
+                                          handle->dequeueReadyTime);
         transactionStats->surfaceStats.emplace_back(surfaceControl, handle->acquireTime,
-                                                    handle->previousReleaseFence);
+                                                    handle->previousReleaseFence,
+                                                    handle->transformHint, eventStats);
     }
     return NO_ERROR;
 }
@@ -239,6 +279,13 @@
             while (transactionStatsItr != transactionStatsDeque.end()) {
                 auto& transactionStats = *transactionStatsItr;
 
+                // If this transaction is still registering, it is not safe to send a callback
+                // because there could be surface controls that haven't been added to
+                // transaction stats or mPendingTransactions.
+                if (isRegisteringTransaction(listener, transactionStats.callbackIds)) {
+                    break;
+                }
+
                 // If we are still waiting on the callback handles for this transaction, stop
                 // here because all transaction callbacks for the same listener must come in order
                 auto pendingTransactions = mPendingTransactions.find(listener);
@@ -262,12 +309,26 @@
             // If the listener has completed transactions
             if (!listenerStats.transactionStats.empty()) {
                 // If the listener is still alive
-                if (IInterface::asBinder(listener)->isBinderAlive()) {
-                    // Send callback
-                    listenerStats.listener->onTransactionCompleted(listenerStats);
-                    IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient);
+                if (listener->isBinderAlive()) {
+                    // Send callback.  The listener stored in listenerStats
+                    // comes from the cross-process setTransactionState call to
+                    // SF.  This MUST be an ITransactionCompletedListener.  We
+                    // keep it as an IBinder due to consistency reasons: if we
+                    // interface_cast at the IPC boundary when reading a Parcel,
+                    // we get pointers that compare unequal in the SF process.
+                    interface_cast<ITransactionCompletedListener>(listenerStats.listener)
+                            ->onTransactionCompleted(listenerStats);
+                    if (transactionStatsDeque.empty()) {
+                        listener->unlinkToDeath(mDeathRecipient);
+                        completedTransactionsItr =
+                                mCompletedTransactions.erase(completedTransactionsItr);
+                    } else {
+                        completedTransactionsItr++;
+                    }
+                } else {
+                    completedTransactionsItr =
+                            mCompletedTransactions.erase(completedTransactionsItr);
                 }
-                completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
             } else {
                 completedTransactionsItr++;
             }
@@ -297,8 +358,11 @@
 
 // -----------------------------------------------------------------------
 
-CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
+CallbackHandle::CallbackHandle(const sp<IBinder>& transactionListener,
                                const std::vector<CallbackId>& ids, const sp<IBinder>& sc)
       : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {}
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index e849f71..f50147a 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -21,6 +21,7 @@
 #include <mutex>
 #include <thread>
 #include <unordered_map>
+#include <unordered_set>
 
 #include <android-base/thread_annotations.h>
 
@@ -30,24 +31,12 @@
 
 namespace android {
 
-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.empty()) ? 0 : callbackIds.front());
-    }
-};
-
 class CallbackHandle : public RefBase {
 public:
-    CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener,
-                   const std::vector<CallbackId>& ids, const sp<IBinder>& sc);
+    CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids,
+                   const sp<IBinder>& sc);
 
-    sp<ITransactionCompletedListener> listener;
+    sp<IBinder> listener;
     std::vector<CallbackId> callbackIds;
     wp<IBinder> surfaceControl;
 
@@ -55,6 +44,12 @@
     sp<Fence> previousReleaseFence;
     nsecs_t acquireTime = -1;
     nsecs_t latchTime = -1;
+    uint32_t transformHint = 0;
+    std::shared_ptr<FenceTime> gpuCompositionDoneFence{FenceTime::NO_FENCE};
+    CompositorTiming compositorTiming;
+    nsecs_t refreshStartTime = 0;
+    nsecs_t dequeueReadyTime = 0;
+    uint64_t frameNumber = 0;
 };
 
 class TransactionCompletedThread {
@@ -64,10 +59,12 @@
     void run();
 
     // Adds listener and callbackIds in case there are no SurfaceControls that are supposed
-    // to be included in the callback. This functions should be call before attempting to add any
-    // callback handles.
-    status_t addCallback(const sp<ITransactionCompletedListener>& transactionListener,
-                         const std::vector<CallbackId>& callbackIds);
+    // to be included in the callback. This functions should be call before attempting to register
+    // any callback handles.
+    status_t startRegistration(const ListenerCallbacks& listenerCallbacks);
+    // Ends the registration. After this is called, no more CallbackHandles will be registered.
+    // It is safe to send a callback if the Transaction doesn't have any Pending callback handles.
+    status_t endRegistration(const ListenerCallbacks& listenerCallbacks);
 
     // 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
@@ -76,11 +73,11 @@
     // presented.
     status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle);
     // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
-    status_t addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
+    status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
 
     // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
     // presented this frame.
-    status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
+    status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
 
     void addPresentFence(const sp<Fence>& presentFence);
 
@@ -89,7 +86,10 @@
 private:
     void threadMain();
 
-    status_t findTransactionStats(const sp<ITransactionCompletedListener>& listener,
+    bool isRegisteringTransaction(const sp<IBinder>& transactionListener,
+                                  const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex);
+
+    status_t findTransactionStats(const sp<IBinder>& listener,
                                   const std::vector<CallbackId>& callbackIds,
                                   TransactionStats** outTransactionStats) REQUIRES(mMutex);
 
@@ -106,13 +106,6 @@
     };
     sp<ThreadDeathRecipient> mDeathRecipient;
 
-    struct ITransactionCompletedListenerHash {
-        std::size_t operator()(const sp<ITransactionCompletedListener>& listener) const {
-            return std::hash<IBinder*>{}((listener) ? IInterface::asBinder(listener).get()
-                                                    : nullptr);
-        }
-    };
-
     // Protects the creation and destruction of mThread
     std::mutex mThreadMutex;
 
@@ -121,13 +114,16 @@
     std::mutex mMutex;
     std::condition_variable_any mConditionVariable;
 
+    std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> mRegisteringTransactions
+            GUARDED_BY(mMutex);
+
     std::unordered_map<
-            sp<ITransactionCompletedListener>,
+            sp<IBinder>,
             std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>,
-            ITransactionCompletedListenerHash>
+            IListenerHash>
             mPendingTransactions GUARDED_BY(mMutex);
-    std::unordered_map<sp<ITransactionCompletedListener>, std::deque<TransactionStats>,
-                       ITransactionCompletedListenerHash>
+
+    std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash>
             mCompletedTransactions GUARDED_BY(mMutex);
 
     bool mRunning GUARDED_BY(mMutex) = false;
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index d3381e5..8fce0c9 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -37,16 +37,6 @@
     return lhs->id < rhs->id;
 }
 
-const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo(
-        const LayersProto& layersProto) {
-    LayerGlobal layerGlobal;
-    layerGlobal.resolution = {layersProto.resolution().w(), layersProto.resolution().h()};
-    layerGlobal.colorMode = layersProto.color_mode();
-    layerGlobal.colorTransform = layersProto.color_transform();
-    layerGlobal.globalTransform = layersProto.global_transform();
-    return layerGlobal;
-}
-
 LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(const LayersProto& layersProto) {
     LayerTree layerTree;
     layerTree.allLayers = generateLayerList(layersProto);
@@ -114,19 +104,17 @@
     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();
+    layer.backgroundBlurRadius = layerProto.background_blur_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());
     }
-
+    layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
+    layer.shadowRadius = layerProto.shadow_radius();
     return layer;
 }
 
@@ -303,6 +291,7 @@
     StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
     StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
     StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str());
+    StringAppendF(&result, "backgroundBlurRadius=%1d, ", backgroundBlurRadius);
     StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
                   static_cast<double>(color.r), static_cast<double>(color.g),
                   static_cast<double>(color.b), static_cast<double>(color.a), flags);
@@ -321,8 +310,9 @@
         first = false;
         result.append(metadata.itemToString(entry.first, ":"));
     }
-    result.append("}");
-
+    result.append("},");
+    StringAppendF(&result, " cornerRadiusCrop=%s, ", cornerRadiusCrop.to_string().c_str());
+    StringAppendF(&result, " shadowRadius=%.3f, ", shadowRadius);
     return result;
 }
 
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index d1b2b1f..52b9165 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -108,25 +108,16 @@
         Transform bufferTransform;
         int32_t queuedFrames;
         bool refreshPending;
-        LayerProtoParser::Rect hwcFrame;
-        LayerProtoParser::FloatRect hwcCrop;
-        int32_t hwcTransform;
-        int32_t hwcCompositionType;
         bool isProtected;
         float cornerRadius;
+        int backgroundBlurRadius;
         LayerMetadata metadata;
+        LayerProtoParser::FloatRect cornerRadiusCrop;
+        float shadowRadius;
 
         std::string to_string() const;
     };
 
-    class LayerGlobal {
-    public:
-        int2 resolution;
-        std::string colorMode;
-        std::string colorTransform;
-        int32_t globalTransform;
-    };
-
     class LayerTree {
     public:
         // all layers in LayersProto and in the original order
@@ -136,7 +127,6 @@
         std::vector<Layer*> topLevelLayers;
     };
 
-    static const LayerGlobal generateLayerGlobalInfo(const LayersProto& layersProto);
     static LayerTree generateLayerTree(const LayersProto& layersProto);
     static std::string layerTreeToString(const LayerTree& layerTree);
 
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index b097505..8afe503 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -7,10 +7,6 @@
 // Contains a list of all layers.
 message LayersProto {
   repeated LayerProto layers = 1;
-  SizeProto resolution = 2;
-  string color_mode = 3;
-  string color_transform = 4;
-  int32 global_transform = 5;
 }
 
 // Information about each layer.
@@ -98,6 +94,18 @@
   FloatRectProto screen_bounds = 46;
 
   InputWindowInfoProto input_window_info = 47;
+
+  // Crop used to draw the rounded corner.
+  FloatRectProto corner_radius_crop = 48;
+
+  // length of the shadow to draw around the layer, it may be set on the
+  // layer or set by a parent layer.
+  float shadow_radius = 49;
+  ColorTransformProto color_transform = 50;
+
+  bool is_relative_of = 51;
+  // Layer's background blur radius in pixels.
+  int32 background_blur_radius = 52;
 }
 
 message PositionProto {
@@ -178,3 +186,8 @@
     bool replace_touchable_region_with_crop = 14;
     RectProto touchable_region_crop = 15;
 }
+
+message ColorTransformProto {
+  // This will be a 4x4 matrix of float values
+  repeated float val = 1;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index e7986d3..d7ad9de 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <sys/resource.h>
 
 #include <sched.h>
@@ -121,3 +125,6 @@
 
     return 0;
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index a4f4285..b19eae6 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -15,7 +15,7 @@
 module: "android.sysprop.SurfaceFlingerProperties"
 owner: Platform
 
-# The following two propertiess define (respectively):
+# The following two properties define (respectively):
 #
 # - The phase offset between hardware vsync and when apps are woken up by the
 #   Choreographer callback
@@ -301,9 +301,19 @@
     prop_name: "ro.surface_flinger.display_primary_white"
 }
 
-# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
-# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
-# refresh rate. Setting this property to 0 means there is no timer.
+# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate
+# switching on the device, e.g. to switch between 60 and 90 Hz. The settings
+# below that are related to refresh rate switching will only have an effect if
+# refresh_rate_switching is enabled.
+prop {
+    api_name: "refresh_rate_switching"
+    type: Boolean
+    scope: System
+    access: Readonly
+    prop_name: "ro.surface_flinger.refresh_rate_switching"
+    deprecated: true
+}
+
 prop {
     api_name: "set_idle_timer_ms"
     type: Integer
@@ -335,14 +345,26 @@
     prop_name: "ro.surface_flinger.set_display_power_timer_ms"
 }
 
+# useContentDetectionForRefreshRate indicates whether Scheduler should detect content FPS, and try
+# to adjust the screen refresh rate based on that.
+prop {
+    api_name: "use_content_detection_for_refresh_rate"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+}
+
 # useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
 # screen refresh rate based on that.
+# Replaced by useContentDetectionForRefreshRate
 prop {
     api_name: "use_smart_90_for_video"
     type: Boolean
     scope: Public
     access: Readonly
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
+    deprecated: true
 }
 
 prop {
@@ -362,3 +384,22 @@
     access: Readonly
     prop_name: "ro.surface_flinger.support_kernel_idle_timer"
 }
+
+# Indicates whether background blurs are supported.
+prop {
+    api_name: "supports_background_blur"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.supports_background_blur"
+}
+
+# Indicates whether Scheduler should use frame rate API when adjusting the
+# display refresh rate.
+prop {
+    api_name: "use_frame_rate_api"
+    type: Boolean
+    scope: Public
+    access: Readonly
+    prop_name: "ro.surface_flinger.use_frame_rate_api"
+}
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index b66e56e..c66523a 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -73,6 +73,11 @@
     enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270"
   }
   prop {
+    api_name: "refresh_rate_switching"
+    prop_name: "ro.surface_flinger.refresh_rate_switching"
+    deprecated: true
+  }
+  prop {
     api_name: "running_without_sync_framework"
     prop_name: "ro.surface_flinger.running_without_sync_framework"
   }
@@ -100,16 +105,29 @@
     prop_name: "ro.surface_flinger.support_kernel_idle_timer"
   }
   prop {
+    api_name: "supports_background_blur"
+    prop_name: "ro.surface_flinger.supports_background_blur"
+  }
+  prop {
     api_name: "use_color_management"
     prop_name: "ro.surface_flinger.use_color_management"
   }
   prop {
+    api_name: "use_content_detection_for_refresh_rate"
+    prop_name: "ro.surface_flinger.use_content_detection_for_refresh_rate"
+  }
+  prop {
     api_name: "use_context_priority"
     prop_name: "ro.surface_flinger.use_context_priority"
   }
   prop {
+    api_name: "use_frame_rate_api"
+    prop_name: "ro.surface_flinger.use_frame_rate_api"
+  }
+  prop {
     api_name: "use_smart_90_for_video"
     prop_name: "ro.surface_flinger.use_smart_90_for_video"
+    deprecated: true
   }
   prop {
     api_name: "use_vr_flinger"
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index 53a3611..94ebe89 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -18,11 +18,24 @@
     test_suites: ["device-tests"],
     srcs: [
         "BufferGenerator.cpp",
+        "CommonTypes_test.cpp",
         "Credentials_test.cpp",
+        "DereferenceSurfaceControl_test.cpp",
+        "DisplayConfigs_test.cpp",
         "InvalidHandles_test.cpp",
+        "LayerCallback_test.cpp",
+        "LayerRenderTypeTransaction_test.cpp",
+        "LayerTransaction_test.cpp",
+        "LayerTypeAndRenderTypeTransaction_test.cpp",
+        "LayerTypeTransaction_test.cpp",
+        "LayerUpdate_test.cpp",
+        "MirrorLayer_test.cpp",
+        "MultiDisplayLayerBounds_test.cpp",
+        "RelativeZ_test.cpp",
+        "SetFrameRate_test.cpp",
+        "SetGeometry_test.cpp",
         "Stress_test.cpp",
         "SurfaceInterceptor_test.cpp",
-        "Transaction_test.cpp",
         "VirtualDisplay_test.cpp",
     ],
     data: ["SurfaceFlinger_test.filter"],
@@ -30,6 +43,52 @@
         "libtrace_proto",
     ],
     shared_libs: [
+        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common@1.2",
+        "android.hardware.graphics.composer@2.1",
+        "libandroid",
+        "libbinder",
+        "libcutils",
+        "libEGL",
+        "libGLESv2",
+        "libgui",
+        "liblayers_proto",
+        "liblog",
+        "libnativewindow",
+        "libprotobuf-cpp-full",
+        "libui",
+        "libutils",
+    ],
+    header_libs: [
+        "libnativewindow_headers",
+    ],
+}
+
+cc_defaults {
+    name: "ipc_defaults",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
+cc_test {
+    name: "IPC_test",
+    defaults: ["ipc_defaults"],
+    test_suites: ["device-tests"],
+    srcs: [
+        "BufferGenerator.cpp",
+        "IPC_test.cpp",
+    ],
+    cppflags: [
+        "-Wall",
+        "-Werror",
+        "-Wformat",
+        "-Wthread-safety",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+    shared_libs: [
         "libandroid",
         "libbinder",
         "libcutils",
@@ -39,17 +98,18 @@
         "liblayers_proto",
         "liblog",
         "libprotobuf-cpp-full",
-        "libtimestats_proto",
         "libui",
         "libutils",
-    ]
-
+    ],
+    cpp_std: "experimental",
+    gnu_extensions: false,
 }
 
 subdirs = [
     "fakehwc",
     "hwc2",
     "unittests",
+    "utils",
     "vsync",
     "waitforvsync",
 ]
diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp
index 8ddda60..293738c 100644
--- a/services/surfaceflinger/tests/BufferGenerator.cpp
+++ b/services/surfaceflinger/tests/BufferGenerator.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gui/BufferItemConsumer.h>
 #include <gui/Surface.h>
 
@@ -379,3 +383,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
new file mode 100644
index 0000000..25b4615
--- /dev/null
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+
+#include <android/data_space.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <android/hardware/graphics/composer/2.1/IComposerClient.h>
+
+using AidlBlendMode = aidl::android::hardware::graphics::common::BlendMode;
+using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+
+using HidlBlendMode = android::hardware::graphics::composer::V2_1::IComposerClient::BlendMode;
+using HidlDataspace = android::hardware::graphics::common::V1_2::Dataspace;
+
+static_assert(static_cast<uint32_t>(AidlBlendMode::INVALID) ==
+              static_cast<uint32_t>(HidlBlendMode::INVALID));
+static_assert(static_cast<uint32_t>(AidlBlendMode::NONE) ==
+              static_cast<uint32_t>(HidlBlendMode::NONE));
+static_assert(static_cast<uint32_t>(AidlBlendMode::PREMULTIPLIED) ==
+              static_cast<uint32_t>(HidlBlendMode::PREMULTIPLIED));
+static_assert(static_cast<uint32_t>(AidlBlendMode::COVERAGE) ==
+              static_cast<uint32_t>(HidlBlendMode::COVERAGE));
+
+static_assert(static_cast<uint32_t>(ADATASPACE_UNKNOWN) ==
+              static_cast<uint32_t>(AidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB_LINEAR) ==
+              static_cast<uint32_t>(AidlDataspace::SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB) == static_cast<uint32_t>(AidlDataspace::SRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB) ==
+              static_cast<uint32_t>(AidlDataspace::SCRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_DISPLAY_P3) ==
+              static_cast<uint32_t>(AidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020_PQ) ==
+              static_cast<uint32_t>(AidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(ADATASPACE_ADOBE_RGB) ==
+              static_cast<uint32_t>(AidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020) ==
+              static_cast<uint32_t>(AidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT709) ==
+              static_cast<uint32_t>(AidlDataspace::BT709));
+static_assert(static_cast<uint32_t>(ADATASPACE_DCI_P3) ==
+              static_cast<uint32_t>(AidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB_LINEAR) ==
+              static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR));
+
+static_assert(static_cast<uint32_t>(ADATASPACE_UNKNOWN) ==
+              static_cast<uint32_t>(HidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_SCRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_DISPLAY_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020_PQ) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(ADATASPACE_ADOBE_RGB) ==
+              static_cast<uint32_t>(HidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(ADATASPACE_BT709) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT709));
+static_assert(static_cast<uint32_t>(ADATASPACE_DCI_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(ADATASPACE_SRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
+
+static_assert(static_cast<uint32_t>(AidlDataspace::UNKNOWN) ==
+              static_cast<uint32_t>(HidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(AidlDataspace::ARBITRARY) ==
+              static_cast<uint32_t>(HidlDataspace::ARBITRARY));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_SHIFT) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_MASK) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_UNSPECIFIED) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT709) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT709));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625_UNADJUSTED) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525_UNADJUSTED) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT470M) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_BT470M));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_FILM) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_FILM));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_DCI_P3) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_ADOBE_RGB) ==
+              static_cast<uint32_t>(HidlDataspace::STANDARD_ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SHIFT) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_MASK) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_UNSPECIFIED) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SRGB) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SMPTE_170M) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_SMPTE_170M));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_2) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_2));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_6) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_6));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_8) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_8));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_ST2084) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_ST2084));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_HLG) ==
+              static_cast<uint32_t>(HidlDataspace::TRANSFER_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_SHIFT) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_MASK) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_UNSPECIFIED) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_FULL) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_FULL));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_LIMITED) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_LIMITED));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_EXTENDED) ==
+              static_cast<uint32_t>(HidlDataspace::RANGE_EXTENDED));
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB) ==
+              static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
+              static_cast<uint32_t>(HidlDataspace::V0_JFIF));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
+              static_cast<uint32_t>(HidlDataspace::V0_BT709));
+static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::DCI_P3_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_P3_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::ADOBE_RGB) ==
+              static_cast<uint32_t>(HidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_LINEAR) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_PQ) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::DEPTH) ==
+              static_cast<uint32_t>(HidlDataspace::DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::SENSOR) ==
+              static_cast<uint32_t>(HidlDataspace::SENSOR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_ITU));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_PQ) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_ITU_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_HLG) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_ITU_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_HLG) ==
+              static_cast<uint32_t>(HidlDataspace::BT2020_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_BT2020) ==
+              static_cast<uint32_t>(HidlDataspace::DISPLAY_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::DYNAMIC_DEPTH) ==
+              static_cast<uint32_t>(HidlDataspace::DYNAMIC_DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::JPEG_APP_SEGMENTS) ==
+              static_cast<uint32_t>(HidlDataspace::JPEG_APP_SEGMENTS));
+static_assert(static_cast<uint32_t>(AidlDataspace::HEIF) ==
+              static_cast<uint32_t>(HidlDataspace::HEIF));
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index b667a74..507d28b 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -1,20 +1,15 @@
-#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 <ui/DisplayConfig.h>
 #include <utils/String8.h>
 
+#include <functional>
+
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
@@ -23,7 +18,6 @@
 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
 
@@ -32,6 +26,10 @@
  * Methods like EnableVsyncInjections and InjectVsync are not tested since they do not
  * return anything meaningful.
  */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
 class CredentialsTest : public ::testing::Test {
 protected:
     void SetUp() override {
@@ -64,14 +62,13 @@
         mDisplay = SurfaceComposerClient::getInternalDisplayToken();
         ASSERT_FALSE(mDisplay == nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info));
-        const ssize_t displayWidth = info.w;
-        const ssize_t displayHeight = info.h;
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
 
         // Background surface
         mBGSurfaceControl =
-                mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight,
+                mComposerClient->createSurface(SURFACE_NAME, config.resolution.getWidth(),
+                                               config.resolution.getHeight(),
                                                PIXEL_FORMAT_RGBA_8888, 0);
         ASSERT_TRUE(mBGSurfaceControl != nullptr);
         ASSERT_TRUE(mBGSurfaceControl->isValid());
@@ -185,10 +182,10 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_TRUE(display != nullptr);
 
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+    DisplayConfig config;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
 
-    Vector<DisplayInfo> configs;
+    Vector<DisplayConfig> configs;
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
 
     ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display));
@@ -215,10 +212,17 @@
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR));
 }
 
-TEST_F(CredentialsTest, SetActiveConfigTest) {
+TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
+    int32_t defaultConfig;
+    float minFps;
+    float maxFps;
+    status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig,
+                                                                       &minFps, &maxFps);
+    ASSERT_EQ(res, NO_ERROR);
     std::function<status_t()> condition = [=]() {
-        return SurfaceComposerClient::setActiveConfig(display, 0);
+        return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig, minFps,
+                                                                   maxFps);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -245,24 +249,13 @@
     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) {
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     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);
+                                         0 /*reqHeight*/, false, ui::ROTATION_0, &outBuffer);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -274,7 +267,7 @@
         sp<GraphicBuffer> outBuffer;
         return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
                                                ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
-                                               Rect(), FRAME_SCALE, &outBuffer);
+                                               Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
     };
     ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
 }
@@ -363,3 +356,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
new file mode 100644
index 0000000..46b98f9
--- /dev/null
+++ b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class DereferenceSurfaceControlTest : public LayerTransactionTest {
+protected:
+    void SetUp() override {
+        LayerTransactionTest::SetUp();
+        bgLayer = createLayer("BG layer", 20, 20);
+        fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
+        fgLayer = createLayer("FG layer", 20, 20);
+        fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
+        Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
+        {
+            SCOPED_TRACE("before anything");
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
+        }
+    }
+    void TearDown() override {
+        LayerTransactionTest::TearDown();
+        bgLayer = 0;
+        fgLayer = 0;
+    }
+
+    sp<SurfaceControl> bgLayer;
+    sp<SurfaceControl> fgLayer;
+};
+
+TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
+    fgLayer = nullptr;
+    {
+        SCOPED_TRACE("after setting null");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 20, 20), Color::RED);
+    }
+}
+
+TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
+    auto transaction = Transaction().show(fgLayer);
+    fgLayer = nullptr;
+    {
+        SCOPED_TRACE("after setting null");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
new file mode 100644
index 0000000..0ed2ffb
--- /dev/null
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <thread>
+#include "LayerTransactionTest.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+/**
+ * Test class for setting display configs and passing around refresh rate ranges.
+ */
+class RefreshRateRangeTest : public ::testing::Test {
+protected:
+    void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+
+    sp<IBinder> mDisplayToken;
+};
+
+TEST_F(RefreshRateRangeTest, setAllConfigs) {
+    int32_t initialDefaultConfig;
+    float initialMin;
+    float initialMax;
+    status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken,
+                                                                       &initialDefaultConfig,
+                                                                       &initialMin, &initialMax);
+    ASSERT_EQ(res, NO_ERROR);
+
+    Vector<DisplayConfig> configs;
+    res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs);
+    ASSERT_EQ(res, NO_ERROR);
+
+    for (size_t i = 0; i < configs.size(); i++) {
+        res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i,
+                                                                  configs[i].refreshRate,
+                                                                  configs[i].refreshRate);
+        ASSERT_EQ(res, NO_ERROR);
+
+        int defaultConfig;
+        float minRefreshRate;
+        float maxRefreshRate;
+        res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig,
+                                                                  &minRefreshRate, &maxRefreshRate);
+        ASSERT_EQ(res, NO_ERROR);
+        ASSERT_EQ(defaultConfig, i);
+        ASSERT_EQ(minRefreshRate, configs[i].refreshRate);
+        ASSERT_EQ(maxRefreshRate, configs[i].refreshRate);
+    }
+
+    res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig,
+                                                              initialMin, initialMax);
+    ASSERT_EQ(res, NO_ERROR);
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp
new file mode 100644
index 0000000..4023c66
--- /dev/null
+++ b/services/surfaceflinger/tests/IPC_test.cpp
@@ -0,0 +1,347 @@
+/*
+ * 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 <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <ui/DisplayConfig.h>
+#include <utils/String8.h>
+
+#include <limits>
+
+#include "BufferGenerator.h"
+#include "utils/CallbackUtils.h"
+#include "utils/ColorUtils.h"
+#include "utils/TransactionUtils.h"
+
+namespace android {
+
+namespace test {
+
+using Transaction = SurfaceComposerClient::Transaction;
+using CallbackInfo = SurfaceComposerClient::CallbackInfo;
+using TCLHash = SurfaceComposerClient::TCLHash;
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class TransactionHelper : public Transaction {
+public:
+    size_t getNumListeners() { return mListenerCallbacks.size(); }
+
+    std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+    getListenerCallbacks() {
+        return mListenerCallbacks;
+    }
+};
+
+class IPCTestUtils {
+public:
+    static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                bool finalState = false);
+    static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence);
+};
+
+class IIPCTest : public IInterface {
+public:
+    DECLARE_META_INTERFACE(IPCTest)
+    enum class Tag : uint32_t {
+        SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
+        InitClient,
+        CreateTransaction,
+        MergeAndApply,
+        VerifyCallbacks,
+        CleanUp,
+        Last,
+    };
+
+    virtual status_t setDeathToken(sp<IBinder>& token) = 0;
+
+    virtual status_t initClient() = 0;
+
+    virtual status_t createTransaction(TransactionHelper* outTransaction, uint32_t width,
+                                       uint32_t height) = 0;
+
+    virtual status_t mergeAndApply(TransactionHelper transaction) = 0;
+
+    virtual status_t verifyCallbacks() = 0;
+
+    virtual status_t cleanUp() = 0;
+};
+
+class BpIPCTest : public SafeBpInterface<IIPCTest> {
+public:
+    explicit BpIPCTest(const sp<IBinder>& impl) : SafeBpInterface<IIPCTest>(impl, "BpIPCTest") {}
+
+    status_t setDeathToken(sp<IBinder>& token) {
+        return callRemote<decltype(&IIPCTest::setDeathToken)>(Tag::SetDeathToken, token);
+    }
+
+    status_t initClient() { return callRemote<decltype(&IIPCTest::initClient)>(Tag::InitClient); }
+
+    status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+        return callRemote<decltype(&IIPCTest::createTransaction)>(Tag::CreateTransaction,
+                                                                  transaction, width, height);
+    }
+
+    status_t mergeAndApply(TransactionHelper transaction) {
+        return callRemote<decltype(&IIPCTest::mergeAndApply)>(Tag::MergeAndApply, transaction);
+    }
+
+    status_t verifyCallbacks() {
+        return callRemote<decltype(&IIPCTest::verifyCallbacks)>(Tag::VerifyCallbacks);
+    }
+
+    status_t cleanUp() { return callRemote<decltype(&IIPCTest::cleanUp)>(Tag::CleanUp); }
+};
+
+IMPLEMENT_META_INTERFACE(IPCTest, "android.gfx.tests.IIPCTest")
+
+class onTestDeath : public IBinder::DeathRecipient {
+public:
+    void binderDied(const wp<IBinder>& /*who*/) override {
+        ALOGE("onTestDeath::binderDied, exiting");
+        exit(0);
+    }
+};
+
+sp<onTestDeath> getDeathToken() {
+    static sp<onTestDeath> token = new onTestDeath;
+    return token;
+}
+
+class BnIPCTest : public SafeBnInterface<IIPCTest> {
+public:
+    BnIPCTest() : SafeBnInterface("BnIPCTest") {}
+
+    status_t setDeathToken(sp<IBinder>& token) override {
+        return token->linkToDeath(getDeathToken());
+    }
+
+    status_t initClient() override {
+        mClient = new SurfaceComposerClient;
+        auto err = mClient->initCheck();
+        return err;
+    }
+
+    status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) {
+        if (transaction == nullptr) {
+            ALOGE("Error in createTransaction: transaction is nullptr");
+            return BAD_VALUE;
+        }
+        mSurfaceControl = mClient->createSurface(String8("parentProcessSurface"), 0, 0,
+                                                 PIXEL_FORMAT_RGBA_8888,
+                                                 ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                 /*parent*/ nullptr);
+        sp<GraphicBuffer> gb;
+        sp<Fence> fence;
+        int err = IPCTestUtils::getBuffer(&gb, &fence);
+        if (err != NO_ERROR) return err;
+
+        TransactionUtils::fillGraphicBufferColor(gb,
+                                                 {0, 0, static_cast<int32_t>(width),
+                                                  static_cast<int32_t>(height)},
+                                                 Color::RED);
+        transaction->setLayerStack(mSurfaceControl, 0)
+                .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max())
+                .setFrame(mSurfaceControl, Rect(0, 0, width, height))
+                .setBuffer(mSurfaceControl, gb)
+                .setAcquireFence(mSurfaceControl, fence)
+                .show(mSurfaceControl)
+                .addTransactionCompletedCallback(mCallbackHelper.function,
+                                                 mCallbackHelper.getContext());
+        return NO_ERROR;
+    }
+
+    status_t mergeAndApply(TransactionHelper /*transaction*/) {
+        // transaction.apply();
+        return NO_ERROR;
+    }
+
+    status_t verifyCallbacks() {
+        ExpectedResult expected;
+        expected.addSurface(ExpectedResult::Transaction::PRESENTED, mSurfaceControl);
+        EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(mCallbackHelper, expected, true));
+        return NO_ERROR;
+    }
+
+    status_t cleanUp() {
+        if (mClient) mClient->dispose();
+        mSurfaceControl = nullptr;
+        IPCThreadState::self()->stopProcess();
+        return NO_ERROR;
+    }
+
+    status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                        uint32_t /*flags*/) override {
+        EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION);
+        EXPECT_LT(code, static_cast<uint32_t>(IIPCTest::Tag::Last));
+        switch (static_cast<IIPCTest::Tag>(code)) {
+            case IIPCTest::Tag::SetDeathToken:
+                return callLocal(data, reply, &IIPCTest::setDeathToken);
+            case IIPCTest::Tag::InitClient:
+                return callLocal(data, reply, &IIPCTest::initClient);
+            case IIPCTest::Tag::CreateTransaction:
+                return callLocal(data, reply, &IIPCTest::createTransaction);
+            case IIPCTest::Tag::MergeAndApply:
+                return callLocal(data, reply, &IIPCTest::mergeAndApply);
+            case IIPCTest::Tag::VerifyCallbacks:
+                return callLocal(data, reply, &IIPCTest::verifyCallbacks);
+            case IIPCTest::Tag::CleanUp:
+                return callLocal(data, reply, &IIPCTest::cleanUp);
+            default:
+                return UNKNOWN_ERROR;
+        }
+    }
+
+private:
+    sp<SurfaceComposerClient> mClient;
+    sp<SurfaceControl> mSurfaceControl;
+    CallbackHelper mCallbackHelper;
+};
+
+class IPCTest : public ::testing::Test {
+public:
+    IPCTest() : mDeathRecipient(new BBinder), mRemote(initRemoteService()) {
+        ProcessState::self()->startThreadPool();
+    }
+    void SetUp() {
+        mClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mPrimaryDisplay = mClient->getInternalDisplayToken();
+        DisplayConfig config;
+        mClient->getActiveDisplayConfig(mPrimaryDisplay, &config);
+        mDisplayWidth = config.resolution.getWidth();
+        mDisplayHeight = config.resolution.getHeight();
+
+        Transaction setupTransaction;
+        setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0);
+        setupTransaction.apply();
+    }
+
+protected:
+    sp<IIPCTest> initRemoteService();
+
+    sp<IBinder> mDeathRecipient;
+    sp<IIPCTest> mRemote;
+    sp<SurfaceComposerClient> mClient;
+    sp<IBinder> mPrimaryDisplay;
+    uint32_t mDisplayWidth;
+    uint32_t mDisplayHeight;
+    sp<SurfaceControl> sc;
+};
+
+status_t IPCTestUtils::getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
+    static BufferGenerator bufferGenerator;
+    return bufferGenerator.get(outBuffer, outFence);
+}
+
+void IPCTestUtils::waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult,
+                                   bool finalState) {
+    CallbackData callbackData;
+    ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+    EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData));
+
+    if (finalState) {
+        ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState());
+    }
+}
+
+sp<IIPCTest> IPCTest::initRemoteService() {
+    static std::mutex mMutex;
+    static sp<IIPCTest> remote;
+    const String16 serviceName("IPCTest");
+
+    std::unique_lock<decltype(mMutex)> lock;
+    if (remote == nullptr) {
+        pid_t forkPid = fork();
+        EXPECT_NE(forkPid, -1);
+
+        if (forkPid == 0) {
+            sp<IIPCTest> nativeService = new BnIPCTest;
+            if (!nativeService) {
+                ALOGE("null service...");
+            }
+            status_t err = defaultServiceManager()->addService(serviceName,
+                                                               IInterface::asBinder(nativeService));
+            if (err != NO_ERROR) {
+                ALOGE("failed to add service: %d", err);
+            }
+            ProcessState::self()->startThreadPool();
+            IPCThreadState::self()->joinThreadPool();
+            [&]() { exit(0); }();
+        }
+        sp<IBinder> binder = defaultServiceManager()->getService(serviceName);
+        remote = interface_cast<IIPCTest>(binder);
+        remote->setDeathToken(mDeathRecipient);
+    }
+    return remote;
+}
+
+TEST_F(IPCTest, MergeBasic) {
+    CallbackHelper helper1;
+    sc = mClient->createSurface(String8("parentProcessSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888,
+                                ISurfaceComposerClient::eFXSurfaceBufferState,
+                                /*parent*/ nullptr);
+    sp<GraphicBuffer> gb;
+    sp<Fence> fence;
+    int err = IPCTestUtils::getBuffer(&gb, &fence);
+    ASSERT_EQ(NO_ERROR, err);
+    TransactionUtils::fillGraphicBufferColor(gb,
+                                             {0, 0, static_cast<int32_t>(mDisplayWidth),
+                                              static_cast<int32_t>(mDisplayHeight)},
+                                             Color::RED);
+
+    Transaction transaction;
+    transaction.setLayerStack(sc, 0)
+            .setLayer(sc, std::numeric_limits<int32_t>::max() - 1)
+            .setBuffer(sc, gb)
+            .setAcquireFence(sc, fence)
+            .show(sc)
+            .addTransactionCompletedCallback(helper1.function, helper1.getContext());
+
+    TransactionHelper remote;
+    mRemote->initClient();
+    mRemote->createTransaction(&remote, mDisplayWidth / 2, mDisplayHeight / 2);
+    ASSERT_EQ(1, remote.getNumListeners());
+    auto remoteListenerCallbacks = remote.getListenerCallbacks();
+    auto remoteCallback = remoteListenerCallbacks.begin();
+    auto remoteCallbackInfo = remoteCallback->second;
+    auto remoteListenerScs = remoteCallbackInfo.surfaceControls;
+    ASSERT_EQ(1, remoteCallbackInfo.callbackIds.size());
+    ASSERT_EQ(1, remoteListenerScs.size());
+
+    sp<SurfaceControl> remoteSc = *(remoteListenerScs.begin());
+    transaction.merge(std::move(remote));
+    transaction.apply();
+
+    sleep(1);
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, sc);
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, remoteSc);
+    EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(helper1, expected, true));
+
+    mRemote->verifyCallbacks();
+    mRemote->cleanUp();
+}
+
+} // namespace test
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
new file mode 100644
index 0000000..6d28e62
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -0,0 +1,879 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+#include "utils/CallbackUtils.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+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, bool setBuffer = true,
+                               bool setBackgroundColor = false) {
+        if (layer) {
+            sp<GraphicBuffer> buffer;
+            sp<Fence> fence;
+            if (setBuffer) {
+                int err = getBuffer(&buffer, &fence);
+                if (err != NO_ERROR) {
+                    return err;
+                }
+
+                transaction.setBuffer(layer, buffer);
+                transaction.setAcquireFence(layer, fence);
+            }
+
+            if (setBackgroundColor) {
+                transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
+                                               ui::Dataspace::UNKNOWN);
+            }
+        }
+
+        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, BufferColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, true, true);
+    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, NoBufferNoColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, false, false);
+    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, BufferNoColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, true, false);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
+}
+
+TEST_F(LayerCallbackTest, NoBufferColor) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+
+    Transaction transaction;
+    CallbackHelper callback;
+    int err = fillTransaction(transaction, &callback, layer, false, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+
+    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
+
+    ExpectedResult expected;
+    expected.addSurface(ExpectedResult::Transaction::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, MergeBufferNoColor) {
+    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, MergeNoBufferColor) {
+    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, false, true);
+    if (err) {
+        GTEST_SUCCEED() << "test not supported";
+        return;
+    }
+    err = fillTransaction(transaction2, &callback2, layer2, false, true);
+    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::NOT_ACQUIRED);
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
+    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
+}
+
+TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
+    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, false, true);
+    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.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
+    expected.addSurface(ExpectedResult::Transaction::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, 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));
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
new file mode 100644
index 0000000..83e5060
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -0,0 +1,1864 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferItemConsumer.h>
+#include <ui/Transform.h>
+#include <thread>
+#include "TransactionTestHarnesses.h"
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::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);
+    void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
+                                  Color finalColor);
+
+protected:
+    LayerRenderPathTestHarness mHarness;
+};
+
+INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest,
+                        ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT));
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    {
+        SCOPED_TRACE("default position");
+        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");
+        const Rect rect(5, 10, 37, 42);
+        auto shot = getScreenCapture();
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // GPU composition requires only 4 bits of subpixel precision during rasterization
+    // XXX GPU composition does not match HWC composition due to precision
+    // loss (b/69315223)
+    const float epsilon = 1.0f / 16.0f;
+    Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
+    {
+        SCOPED_TRACE("rounding down");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
+    {
+        SCOPED_TRACE("rounding up");
+        getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setPosition(layer, -32, -32).apply();
+    {
+        SCOPED_TRACE("negative coordinates");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+
+    Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
+    {
+        SCOPED_TRACE("positive coordinates");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // partially out of bounds
+    Transaction().setPosition(layer, -30, -30).apply();
+    {
+        SCOPED_TRACE("negative coordinates");
+        getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
+    }
+
+    Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
+    {
+        SCOPED_TRACE("positive coordinates");
+        getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
+                                             mDisplayHeight),
+                                        Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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 = getScreenCapture();
+        const Rect rect(5, 10, 37, 42);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+    {
+        SCOPED_TRACE("resize applied");
+        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setSize(layer, 64, 64).apply();
+    {
+        SCOPED_TRACE("resize pending");
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 32, 32);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+    {
+        SCOPED_TRACE("resize applied");
+        auto shot = getScreenCapture();
+        const Rect rect(0, 0, 64, 64);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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();
+    getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) {
+    uint32_t transformHint = ui::Transform::ROT_INVALID;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32,
+                                                ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                /*parent*/ nullptr, &transformHint));
+    ASSERT_NE(ui::Transform::ROT_INVALID, transformHint);
+}
+
+void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    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));
+
+    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 = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
+        shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
+    }
+
+    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
+    {
+        SCOPED_TRACE("layerG below");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) {
+    ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState));
+}
+
+void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
+    sp<SurfaceControl> layerR;
+    sp<SurfaceControl> layerG;
+    sp<SurfaceControl> layerB;
+    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
+    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 = 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);
+    }
+
+    // layerR = 4, layerG = layerR + 3, layerB = 2
+    Transaction().setLayer(layerR, mLayerZBase + 4).apply();
+    {
+        SCOPED_TRACE("layerB < (layerR < layerG)");
+        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);
+    }
+
+    // layerR = 4, layerG = layerR - 3, layerB = 2
+    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
+    {
+        SCOPED_TRACE("layerB < (layerG < layerR)");
+        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);
+    }
+
+    // restore to absolute z
+    // layerR = 4, layerG = 0, layerB = 2
+    Transaction().setLayer(layerG, mLayerZBase).apply();
+    {
+        SCOPED_TRACE("layerG < layerB < layerR");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+        shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
+    }
+
+    // layerR should not affect layerG anymore
+    // layerR = 1, layerG = 0, layerB = 2
+    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
+    {
+        SCOPED_TRACE("layerG < layerR < layerB");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
+        shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
+    }
+}
+
+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(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 = getBufferQueueLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::RED));
+    // setTransparentRegionHint always applies to the following buffer
+    Transaction().setTransparentRegionHint(layer, Region(top)).apply();
+    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
+    {
+        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 pending");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::BLACK);
+        shot->expectColor(bottom, Color::RED);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer));
+    {
+        SCOPED_TRACE("bottom transparent");
+        auto shot = getScreenCapture();
+        shot->expectColor(top, Color::RED);
+        shot->expectColor(bottom, Color::BLACK);
+    }
+}
+
+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(
+            TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT));
+    ASSERT_NO_FATAL_FAILURE(TransactionUtils::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(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED));
+    ASSERT_NO_FATAL_FAILURE(
+            TransactionUtils::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));
+    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
+
+    // check that transparent region hint is bound by the layer size
+    Transaction()
+            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
+            .setPosition(layerR, 16, 16)
+            .setLayer(layerR, mLayerZBase + 1)
+            .apply();
+    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_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);
+}
+
+void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
+    sp<SurfaceControl> layer1;
+    sp<SurfaceControl> layer2;
+    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));
+
+    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 = 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});
+        shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255});
+
+        r /= 4; // r * (1.0f - 0.75f)
+        shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255});
+    }
+}
+
+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(LayerRenderTypeTransactionTest, SetColorBasic) {
+    sp<SurfaceControl> bufferLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
+
+    {
+        SCOPED_TRACE("default color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
+    const Color expected = {15, 51, 85, 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).apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
+    }
+}
+
+// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
+// BLUE: prior background color
+// GREEN: final background color
+// BLACK: no color or fill
+void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
+                                                              bool bufferFill, float alpha,
+                                                              Color finalColor) {
+    sp<SurfaceControl> layer;
+    int32_t width = 500;
+    int32_t height = 500;
+
+    Color fillColor = Color::RED;
+    Color priorBgColor = Color::BLUE;
+    Color expectedColor = Color::BLACK;
+    switch (layerType) {
+        case ISurfaceComposerClient::eFXSurfaceEffect:
+            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
+            Transaction()
+                    .setCrop_legacy(layer, Rect(0, 0, width, height))
+                    .setColor(layer, half3(1.0f, 0, 0))
+                    .apply();
+            expectedColor = fillColor;
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
+            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+            if (bufferFill) {
+                ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
+                expectedColor = fillColor;
+            }
+            Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
+            break;
+        case ISurfaceComposerClient::eFXSurfaceBufferState:
+            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
+            if (bufferFill) {
+                ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
+                expectedColor = fillColor;
+            }
+            Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
+            break;
+        default:
+            GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
+            return;
+    }
+
+    if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceEffect) {
+        Transaction()
+                .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
+                .apply();
+        if (!bufferFill) {
+            expectedColor = priorBgColor;
+        }
+    }
+
+    {
+        SCOPED_TRACE("default before setting background color layer");
+        screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
+    }
+    Transaction()
+            .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
+            .apply();
+
+    {
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, width, height), finalColor);
+        shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceEffect,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = true;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
+    bool priorColor = true;
+    bool bufferFill = true;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = true;
+    float alpha = 1.0f;
+    Color finalColor = Color::RED;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 1.0f;
+    Color finalColor = Color::GREEN;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
+    bool priorColor = false;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest,
+       SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
+    bool priorColor = true;
+    bool bufferFill = false;
+    float alpha = 0;
+    Color finalColor = Color::BLACK;
+    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
+                                                     priorColor, bufferFill, alpha, finalColor));
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setColor(colorLayer, half3(2.0f, 0.0f, 0.0f))
+            .apply();
+
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+// An invalid color will not render a color and the layer will not be visible.
+TEST_P(LayerRenderTypeTransactionTest, SetInvalidColor) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setColor(colorLayer, half3(1.0f, -1.0f, 0.5f))
+            .apply();
+
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) {
+    sp<SurfaceControl> bufferLayer;
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    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);
+    // 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)
+            .setAlpha(colorLayer, alpha)
+            .setLayer(colorLayer, mLayerZBase + 1)
+            .apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
+}
+
+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(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */,
+                                                     0 /* buffer height */,
+                                                     ISurfaceComposerClient::eFXSurfaceEffect));
+    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);
+    // this is handwavy, but the precision loss scaled by 255 (8-bit per
+    // channel) should be less than one
+    const uint8_t tolerance = 1;
+    Transaction()
+            .reparent(colorLayer, parentLayer->getHandle())
+            .setColor(colorLayer, color)
+            .setAlpha(parentLayer, alpha)
+            .setLayer(parentLayer, mLayerZBase + 1)
+            .apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
+                                    tolerance);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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");
+        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");
+        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");
+        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");
+        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");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
+    }
+}
+
+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(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 = getScreenCapture();
+    // check a 8x8 region inside each color
+    auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
+        const int32_t halfL = 4;
+        return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL);
+    };
+    const int32_t unit = int32_t(trans / 2);
+    shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED);
+    shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN);
+    shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE);
+    shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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 = getScreenCapture();
+        const Rect rect(0, 0, 32, 32);
+        shot->expectColor(rect, Color::RED);
+        shot->expectBorder(rect, Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
+    {
+        SCOPED_TRACE("resize applied");
+        const Rect rect(0, 0, 128, 128);
+        getScreenCapture()->expectColor(rect, Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
+    Transaction()
+            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
+            .setSize(layer, 64, 64)
+            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+            .apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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
+    // (b/69315456)
+    Transaction()
+            .setSize(layer, 64, 16)
+            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
+            .apply();
+    {
+        SCOPED_TRACE("SCALE_TO_WINDOW");
+        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
+                                           Color::BLUE, Color::WHITE, true /* filtered */);
+    }
+}
+
+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_legacy(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(crop, Color::RED);
+    shot->expectBorder(crop, Color::BLACK);
+}
+
+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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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();
+        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    }
+
+    {
+        SCOPED_TRACE("negative rect");
+        Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    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_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
+    sp<GraphicBuffer> buffer =
+            new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
+                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                      BufferUsage::COMPOSER_OVERLAY,
+                              "test");
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
+    TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
+
+    Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
+
+    Transaction().setBuffer(layer, buffer).apply();
+
+    // Partially out of bounds in the negative (upper left) direction
+    Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply();
+    {
+        SCOPED_TRACE("out of bounds, negative (upper left) direction");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+    }
+
+    // Partially out of bounds in the positive (lower right) direction
+    Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply();
+    {
+        SCOPED_TRACE("out of bounds, positive (lower right) direction");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
+        shot->expectBorder(Rect(0, 0, 64, 64), 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, 64, 16), Color::BLUE);
+        shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
+        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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_legacy(layer, crop).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(crop + position, Color::RED);
+    shot->expectBorder(crop + position, Color::BLACK);
+}
+
+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(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // crop_legacy is affected by matrix
+    Transaction()
+            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
+            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
+            .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, SetCropWithResize_BufferQueue) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    // 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 = getScreenCapture();
+        shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
+        shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
+    }
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
+    {
+        SCOPED_TRACE("resize applied");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
+        shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_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(8, 8, 24, 24);
+
+    Transaction().setFrame(layer, frame).apply();
+    auto shot = getScreenCapture();
+    shot->expectColor(frame, Color::RED);
+    shot->expectBorder(frame, Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_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().setFrame(layer, Rect(8, 8, 8, 8)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    {
+        SCOPED_TRACE("negative rect");
+        Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::BLUE);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
+        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, SetBufferCaching_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+    std::array<sp<GraphicBuffer>, 10> buffers;
+
+    size_t idx = 0;
+    for (auto& buffer : buffers) {
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                           BufferUsage::COMPOSER_OVERLAY,
+                                   "test");
+        Color color = colors[idx % colors.size()];
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+        idx++;
+    }
+
+    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+    // cache is working.
+    idx = 0;
+    for (auto& buffer : buffers) {
+        for (int i = 0; i < 2; i++) {
+            Transaction().setBuffer(layer, buffer).apply();
+
+            Color color = colors[idx % colors.size()];
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        }
+        idx++;
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+    std::array<sp<GraphicBuffer>, 70> buffers;
+
+    size_t idx = 0;
+    for (auto& buffer : buffers) {
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                           BufferUsage::COMPOSER_OVERLAY,
+                                   "test");
+        Color color = colors[idx % colors.size()];
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+        idx++;
+    }
+
+    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+    // cache is working.
+    idx = 0;
+    for (auto& buffer : buffers) {
+        for (int i = 0; i < 2; i++) {
+            Transaction().setBuffer(layer, buffer).apply();
+
+            Color color = colors[idx % colors.size()];
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        }
+        idx++;
+    }
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(
+            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
+
+    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
+
+    std::array<sp<GraphicBuffer>, 65> buffers;
+
+    size_t idx = 0;
+    for (auto& buffer : buffers) {
+        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
+                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+                                           BufferUsage::COMPOSER_OVERLAY,
+                                   "test");
+        Color color = colors[idx % colors.size()];
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
+        idx++;
+    }
+
+    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
+    // cache is working.
+    idx = 0;
+    for (auto& buffer : buffers) {
+        for (int i = 0; i < 2; i++) {
+            Transaction().setBuffer(layer, buffer).apply();
+
+            Color color = colors[idx % colors.size()];
+            auto shot = screenshot();
+            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
+            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+        }
+        if (idx == 0) {
+            buffers[0].clear();
+        }
+        idx++;
+    }
+}
+
+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_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");
+    TransactionUtils::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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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");
+    TransactionUtils::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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+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");
+    TransactionUtils::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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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");
+    TransactionUtils::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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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");
+    TransactionUtils::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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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");
+    TransactionUtils::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, mDisplayWidth, mDisplayHeight), Color::RED);
+    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
+}
+
+TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) {
+    sp<SurfaceControl> colorLayer;
+    ASSERT_NO_FATAL_FAILURE(colorLayer =
+                                    createLayer("test", 0 /* buffer width */, 0 /* buffer height */,
+                                                ISurfaceComposerClient::eFXSurfaceEffect));
+    Transaction()
+            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
+            .setLayer(colorLayer, 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 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(colorLayer, matrix, vec3()).apply();
+    {
+        SCOPED_TRACE("new color");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
+    }
+}
+
+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::eFXSurfaceEffect, 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 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::eFXSurfaceEffect, 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);
+    }
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
new file mode 100644
index 0000000..932c7c8
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <hardware/hwcomposer_defs.h>
+#include <private/gui/ComposerService.h>
+#include <ui/DisplayConfig.h>
+
+#include "BufferGenerator.h"
+#include "utils/ScreenshotUtils.h"
+#include "utils/TransactionUtils.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTransactionTest : public ::testing::Test {
+protected:
+    void SetUp() override {
+        mClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
+
+        ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
+
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
+    }
+
+    virtual void TearDown() {
+        mBlackBgSurface = 0;
+        mClient->dispose();
+        mClient = 0;
+    }
+
+    virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client,
+                                           const char* name, uint32_t width, uint32_t height,
+                                           uint32_t flags = 0, SurfaceControl* parent = nullptr,
+                                           uint32_t* outTransformHint = nullptr) {
+        auto layer = createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags,
+                                   parent, outTransformHint);
+
+        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();
+        }
+
+        return 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,
+                                             uint32_t* outTransformHint = nullptr) {
+        auto layer = client->createSurface(String8(name), width, height, format, flags, parent,
+                                           LayerMetadata(), outTransformHint);
+        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,
+                                           uint32_t* outTransformHint = nullptr) {
+        return createLayer(mClient, name, width, height, flags, parent, outTransformHint);
+    }
+
+    sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
+                                        SurfaceControl* parent = nullptr) {
+        auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
+                                        PIXEL_FORMAT_RGBA_8888,
+                                        ISurfaceComposerClient::eFXSurfaceEffect, parent);
+        asTransaction([&](Transaction& t) {
+            t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
+            t.setAlpha(colorLayer, color.a / 255.0f);
+        });
+        return colorLayer;
+    }
+
+    ANativeWindow_Buffer getBufferQueueLayerBuffer(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));
+
+        return buffer;
+    }
+
+    void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
+        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
+
+        // wait for the newly posted buffer to be latched
+        waitForLayerBuffers();
+    }
+
+    virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
+                                           int32_t bufferWidth, int32_t bufferHeight) {
+        ANativeWindow_Buffer buffer;
+        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
+        TransactionUtils::fillANativeWindowBufferColor(buffer,
+                                                       Rect(0, 0, bufferWidth, bufferHeight),
+                                                       color);
+        postBufferQueueLayerBuffer(layer);
+    }
+
+    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");
+        TransactionUtils::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) {
+        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;
+        }
+    }
+
+    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;
+        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
+                                                       topRight);
+        TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
+                                                       bottomLeft);
+        TransactionUtils::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;
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft);
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH),
+                                                 topRight);
+        TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight),
+                                                 bottomLeft);
+        TransactionUtils::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;
+    }
+
+    void asTransaction(const std::function<void(Transaction&)>& exec) {
+        Transaction t;
+        exec(t);
+        t.apply(true);
+    }
+
+    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->getInternalDisplayToken();
+        ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(mDisplay, &config));
+        mDisplayRect = Rect(config.resolution);
+        mDisplayWidth = mDisplayRect.getWidth();
+        mDisplayHeight = mDisplayRect.getHeight();
+
+        // 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 = static_cast<int32_t>(1e6 / config.refreshRate) * 3;
+
+        mDisplayLayerStack = 0;
+
+        mBlackBgSurface =
+                createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */,
+                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
+
+        // 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() {
+        // 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;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp
new file mode 100644
index 0000000..97cba63
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <private/android_filesystem_config.h>
+#include <thread>
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
+
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<GraphicBuffer> outBuffer;
+    Transaction()
+            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+            .apply(true);
+    ASSERT_EQ(PERMISSION_DENIED,
+              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+    UIDFaker f(AID_SYSTEM);
+
+    // By default the system can capture screenshots with secure layers but they
+    // will be blacked out
+    ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
+
+    {
+        SCOPED_TRACE("as system");
+        auto shot = screenshot();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+
+    // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
+    // to receive them...we are expected to take care with the results.
+    bool outCapturedSecureLayers;
+    ASSERT_EQ(NO_ERROR,
+              composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
+                                      ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
+                                      0, false, ui::ROTATION_0, true));
+    ASSERT_EQ(true, outCapturedSecureLayers);
+    ScreenCapture sc(outBuffer);
+    sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+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_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, 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);
+    }
+}
+
+// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge
+// the dropped buffer's damage region into the next buffer's damage region. If
+// we don't do this, we'll report an incorrect damage region to hardware
+// composer, resulting in broken rendering. This test checks the BufferQueue
+// case.
+//
+// Unfortunately, we don't currently have a way to inspect the damage region
+// SurfaceFlinger sends to hardware composer from a test, so this test requires
+// the dev to manually watch the device's screen during the test to spot broken
+// rendering. Because the results can't be automatically verified, this test is
+// marked disabled.
+TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) {
+    const int width = mDisplayWidth;
+    const int height = mDisplayHeight;
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
+    const auto producer = layer->getIGraphicBufferProducer();
+    const sp<IProducerListener> dummyListener(new DummyProducerListener);
+    IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
+    ASSERT_EQ(OK,
+              producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
+
+    std::map<int, sp<GraphicBuffer>> slotMap;
+    auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
+        ASSERT_NE(nullptr, buf);
+        const auto iter = slotMap.find(slot);
+        ASSERT_NE(slotMap.end(), iter);
+        *buf = iter->second;
+    };
+
+    auto dequeue = [&](int* outSlot) {
+        ASSERT_NE(nullptr, outSlot);
+        *outSlot = -1;
+        int slot;
+        sp<Fence> fence;
+        uint64_t age;
+        FrameEventHistoryDelta timestamps;
+        const status_t dequeueResult =
+                producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888,
+                                        GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                        &age, &timestamps);
+        if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
+            sp<GraphicBuffer> newBuf;
+            ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf));
+            ASSERT_NE(nullptr, newBuf.get());
+            slotMap[slot] = newBuf;
+        } else {
+            ASSERT_EQ(OK, dequeueResult);
+        }
+        *outSlot = slot;
+    };
+
+    auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) {
+        IGraphicBufferProducer::QueueBufferInput input(
+                /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN,
+                /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
+                /*transform=*/0, Fence::NO_FENCE);
+        input.setSurfaceDamage(damage);
+        IGraphicBufferProducer::QueueBufferOutput output;
+        ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output));
+    };
+
+    auto fillAndPostBuffers = [&](const Color& color) {
+        int slot1;
+        ASSERT_NO_FATAL_FAILURE(dequeue(&slot1));
+        int slot2;
+        ASSERT_NO_FATAL_FAILURE(dequeue(&slot2));
+
+        sp<GraphicBuffer> buf1;
+        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1));
+        sp<GraphicBuffer> buf2;
+        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2));
+        TransactionUtils::fillGraphicBufferColor(buf1, Rect(width, height), color);
+        TransactionUtils::fillGraphicBufferColor(buf2, Rect(width, height), color);
+
+        const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100);
+        ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime));
+        ASSERT_NO_FATAL_FAILURE(
+                queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)),
+                      displayTime));
+    };
+
+    const auto startTime = systemTime();
+    const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE};
+    int colorIndex = 0;
+    while (nanoseconds_to_seconds(systemTime() - startTime) < 10) {
+        ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()]));
+        std::this_thread::sleep_for(1s);
+    }
+
+    ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
new file mode 100644
index 0000000..dbace11
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp
@@ -0,0 +1,370 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <cutils/properties.h>
+#include <gui/BufferItemConsumer.h>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+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;
+};
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+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)));
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
+    // cannot test robustness against invalid sizes (zero or really huge)
+}
+
+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, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
+    {
+        SCOPED_TRACE("layerR");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    Transaction().setLayer(layerG, mLayerZBase + 2).apply();
+    {
+        SCOPED_TRACE("layerG");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+    }
+}
+
+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, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+    Transaction()
+            .setPosition(layerG, 16, 16)
+            .setRelativeLayer(layerG, layerR->getHandle(), 1)
+            .apply();
+
+    layerG.clear();
+    // layerG should have been removed
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    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");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+
+    Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
+    {
+        SCOPED_TRACE("layer shown");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+}
+
+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, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
+
+    Transaction()
+            .setLayer(layerR, mLayerZBase + 1)
+            .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
+            .apply();
+    {
+        SCOPED_TRACE("layerR opaque");
+        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);
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
+    }
+}
+
+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, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    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");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+
+    Transaction().setLayer(layerR, -3).apply();
+    {
+        SCOPED_TRACE("layerG");
+        auto shot = getScreenCapture();
+        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
+    }
+}
+
+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, 32, 32));
+
+    Transaction().setAlpha(layer, 2.0f).apply();
+    {
+        SCOPED_TRACE("clamped to 1.0f");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
+    }
+
+    Transaction().setAlpha(layer, -1.0f).apply();
+    {
+        SCOPED_TRACE("clamped to 0.0f");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
+    }
+}
+
+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));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Transaction()
+                .setCornerRadius(layer, cornerRadius)
+                .setCrop_legacy(layer, Rect(0, 0, size, size))
+                .apply();
+    } else {
+        Transaction()
+                .setCornerRadius(layer, cornerRadius)
+                .setFrame(layer, Rect(0, 0, size, size))
+                .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));
+
+    if (mLayerType == ISurfaceComposerClient::eFXSurfaceBufferQueue) {
+        Transaction()
+                .setCornerRadius(parent, cornerRadius)
+                .setCrop_legacy(parent, Rect(0, 0, size, size))
+                .reparent(child, parent->getHandle())
+                .setPosition(child, 0, size / 2)
+                .apply();
+    } else {
+        Transaction()
+                .setCornerRadius(parent, cornerRadius)
+                .setFrame(parent, Rect(0, 0, size, size))
+                .reparent(child, parent->getHandle())
+                .setFrame(child, Rect(0, size / 2, size, size))
+                .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(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadius) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    auto size = 256;
+    auto center = size / 2;
+    auto blurRadius = 50;
+
+    sp<SurfaceControl> backgroundLayer;
+    ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+    sp<SurfaceControl> leftLayer;
+    ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+    sp<SurfaceControl> blurLayer;
+    ASSERT_NO_FATAL_FAILURE(blurLayer = createLayer("blur", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(blurLayer, Color::TRANSPARENT, size, size));
+
+    Transaction().setBackgroundBlurRadius(blurLayer, blurRadius).apply();
+
+    auto shot = getScreenCapture();
+    // Edges are mixed
+    shot->expectColor(Rect(center - 1, center - 5, center, center + 5), Color{150, 150, 0, 255},
+                      50 /* tolerance */);
+    shot->expectColor(Rect(center, center - 5, center + 1, center + 5), Color{150, 150, 0, 255},
+                      50 /* tolerance */);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetBackgroundBlurRadiusOnMultipleLayers) {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.surface_flinger.supports_background_blur", value, "0");
+    if (!atoi(value)) {
+        // This device doesn't support blurs, no-op.
+        return;
+    }
+
+    auto size = 256;
+    auto center = size / 2;
+    auto blurRadius = 50;
+
+    sp<SurfaceControl> backgroundLayer;
+    ASSERT_NO_FATAL_FAILURE(backgroundLayer = createLayer("background", size, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(backgroundLayer, Color::GREEN, size, size));
+
+    sp<SurfaceControl> leftLayer;
+    ASSERT_NO_FATAL_FAILURE(leftLayer = createLayer("left", size / 2, size));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(leftLayer, Color::RED, size / 2, size));
+
+    sp<SurfaceControl> blurLayer1;
+    auto centralSquareSize = size / 2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer1 =
+                                    createLayer("blur1", centralSquareSize, centralSquareSize));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer1, Color::BLUE, centralSquareSize, centralSquareSize));
+
+    sp<SurfaceControl> blurLayer2;
+    ASSERT_NO_FATAL_FAILURE(blurLayer2 = createLayer("blur2", size, size));
+    ASSERT_NO_FATAL_FAILURE(
+            fillLayerColor(blurLayer2, Color::TRANSPARENT, centralSquareSize, centralSquareSize));
+
+    Transaction()
+            .setBackgroundBlurRadius(blurLayer1, blurRadius)
+            .setBackgroundBlurRadius(blurLayer2, blurRadius)
+            .apply();
+
+    auto shot = getScreenCapture();
+    shot->expectColor(Rect(center - 5, center - 5, center, center), Color{100, 100, 100, 255},
+                      40 /* tolerance */);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
+    sp<SurfaceControl> bufferLayer;
+    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
+
+    // color is ignored
+    Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
+    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
+TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+    Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
+    {
+        SCOPED_TRACE("non-existing layer stack");
+        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
+    }
+
+    Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
+    {
+        SCOPED_TRACE("original layer stack");
+        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
+    }
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
new file mode 100644
index 0000000..84780ba
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <gui/BufferItemConsumer.h>
+#include "TransactionTestHarnesses.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class LayerTypeTransactionTest : public LayerTypeTransactionHarness,
+                                 public ::testing::WithParamInterface<uint32_t> {
+public:
+    LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {}
+};
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+INSTANTIATE_TEST_CASE_P(
+        LayerTypeTransactionTests, LayerTypeTransactionTest,
+        ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue),
+                          static_cast<uint32_t>(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, 32, 32));
+    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
+    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, 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();
+
+    std::unique_ptr<ScreenCapture> screenshot;
+    // only layerB is in this range
+    sp<IBinder> parentHandle = parent->getHandle();
+    ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
+    screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
+}
+
+TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceEffect);
+
+    sp<SurfaceControl> childLayer;
+    ASSERT_NO_FATAL_FAILURE(
+            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+                                                           0 /* buffer height */,
+                                                           ISurfaceComposerClient::eFXSurfaceEffect,
+                                                           parent.get()));
+    Transaction()
+            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+            .show(childLayer)
+            .show(parent)
+            .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
+            .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
+            .apply();
+
+    Transaction()
+            .setRelativeLayer(childLayer, parent->getHandle(), -1)
+            .setLayer(childLayer, 1)
+            .apply();
+
+    {
+        SCOPED_TRACE("setLayer above");
+        // Set layer should get applied and place the child above.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+    }
+
+    Transaction()
+            .setLayer(childLayer, 1)
+            .setRelativeLayer(childLayer, parent->getHandle(), -1)
+            .apply();
+
+    {
+        SCOPED_TRACE("setRelative below");
+        // Set relative layer should get applied and place the child below.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+    }
+}
+
+TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) {
+    sp<SurfaceControl> parent =
+            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceEffect);
+    sp<SurfaceControl> relativeParent =
+            LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */,
+                                              0 /* buffer height */,
+                                              ISurfaceComposerClient::eFXSurfaceEffect);
+
+    sp<SurfaceControl> childLayer;
+    ASSERT_NO_FATAL_FAILURE(
+            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
+                                                           0 /* buffer height */,
+                                                           ISurfaceComposerClient::eFXSurfaceEffect,
+                                                           parent.get()));
+    Transaction()
+            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
+            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
+            .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f})
+            .show(childLayer)
+            .show(parent)
+            .show(relativeParent)
+            .setLayer(parent, mLayerZBase - 1)
+            .setLayer(relativeParent, mLayerZBase)
+            .apply();
+
+    Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply();
+
+    {
+        SCOPED_TRACE("setLayer above");
+        // Set layer should get applied and place the child above.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
+    }
+
+    Transaction().hide(relativeParent).apply();
+
+    {
+        SCOPED_TRACE("hide relative parent");
+        // The relative should no longer be visible.
+        std::unique_ptr<ScreenCapture> screenshot;
+        ScreenCapture::captureScreen(&screenshot);
+        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
+    }
+}
+
+TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
+
+    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
+    sp<GraphicBuffer> outBuffer;
+    Transaction()
+            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+            .apply(true);
+    ASSERT_EQ(PERMISSION_DENIED,
+              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, false));
+}
+TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
+    sp<SurfaceControl> layer;
+    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
+
+    sp<IBinder> handle = layer->getHandle();
+    ASSERT_TRUE(handle != nullptr);
+
+    FrameStats frameStats;
+    mClient->getLayerFrameStats(handle, &frameStats);
+
+    ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0));
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
new file mode 100644
index 0000000..cf3f8e8
--- /dev/null
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -0,0 +1,1717 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class LayerUpdateTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        const ui::Size& resolution = config.resolution;
+
+        // Background surface
+        mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
+                                        resolution.getHeight(), 0);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+        TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195);
+
+        // Foreground surface
+        mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0);
+
+        ASSERT_TRUE(mFGSurfaceControl != nullptr);
+        ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+
+        // Synchronization surface
+        mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
+        ASSERT_TRUE(mSyncSurfaceControl != nullptr);
+        ASSERT_TRUE(mSyncSurfaceControl->isValid());
+
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+
+            t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
+
+            t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
+                    .setPosition(mFGSurfaceControl, 64, 64)
+                    .show(mFGSurfaceControl);
+
+            t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
+                    .setPosition(mSyncSurfaceControl, resolution.getWidth() - 2,
+                                 resolution.getHeight() - 2)
+                    .show(mSyncSurfaceControl);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mBGSurfaceControl = 0;
+        mFGSurfaceControl = 0;
+        mSyncSurfaceControl = 0;
+    }
+
+    void waitForPostedBuffers() {
+        // Since the sync surface is in synchronous mode (i.e. double buffered)
+        // posting three buffers to it should ensure that at least two
+        // SurfaceFlinger::handlePageFlip calls have been made, which should
+        // guaranteed that a buffer posted to another Surface has been retired.
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+        TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    }
+
+    sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
+
+    // This surface is used to ensure that the buffers posted to
+    // mFGSurfaceControl have been picked up by SurfaceFlinger.
+    sp<SurfaceControl> mSyncSurfaceControl;
+};
+
+TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
+    TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10);
+    waitForPostedBuffers();
+
+    Transaction{}
+            .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
+            .setPosition(relative, 64, 64)
+            .apply();
+
+    {
+        // The relative should be on top of the FG control.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(64, 64, 10, 10, 10);
+    }
+    Transaction{}.detachChildren(mFGSurfaceControl).apply();
+
+    {
+        // Nothing should change at this point.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(64, 64, 10, 10, 10);
+    }
+
+    Transaction{}.hide(relative).apply();
+
+    {
+        // Ensure that the relative was actually hidden, rather than
+        // being left in the detached but visible state.
+        ScreenCapture::captureScreen(&sc);
+        sc->expectFGColor(64, 64);
+    }
+}
+
+class GeometryLatchingTest : public LayerUpdateTest {
+protected:
+    void EXPECT_INITIAL_STATE(const char* trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // We find the leading edge of the FG surface.
+        sc->expectFGColor(127, 127);
+        sc->expectBGColor(128, 128);
+    }
+
+    void lockAndFillFGBuffer() {
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false);
+    }
+
+    void unlockFGBuffer() {
+        sp<Surface> s = mFGSurfaceControl->getSurface();
+        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+        waitForPostedBuffers();
+    }
+
+    void completeFGResize() {
+        TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+        waitForPostedBuffers();
+    }
+    void restoreInitialState() {
+        asTransaction([&](Transaction& t) {
+            t.setSize(mFGSurfaceControl, 64, 64);
+            t.setPosition(mFGSurfaceControl, 64, 64);
+            t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+        });
+
+        EXPECT_INITIAL_STATE("After restoring initial state");
+    }
+    std::unique_ptr<ScreenCapture> sc;
+};
+
+class CropLatchingTest : public GeometryLatchingTest {
+protected:
+    void EXPECT_CROPPED_STATE(const char* trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // The edge should be moved back one pixel by our crop.
+        sc->expectFGColor(126, 126);
+        sc->expectBGColor(127, 127);
+        sc->expectBGColor(128, 128);
+    }
+
+    void EXPECT_RESIZE_STATE(const char* trace) {
+        SCOPED_TRACE(trace);
+        ScreenCapture::captureScreen(&sc);
+        // The FG is now resized too 128,128 at 64,64
+        sc->expectFGColor(64, 64);
+        sc->expectFGColor(191, 191);
+        sc->expectBGColor(192, 192);
+    }
+};
+
+TEST_F(LayerUpdateTest, DeferredTransactionTest) {
+    std::unique_ptr<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before anything");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
+    }
+
+    // set up two deferred transactions on different frames
+    asTransaction([&](Transaction& t) {
+        t.setAlpha(mFGSurfaceControl, 0.75);
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber());
+    });
+
+    asTransaction([&](Transaction& t) {
+        t.setPosition(mFGSurfaceControl, 128, 128);
+        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
+                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
+    });
+
+    {
+        SCOPED_TRACE("before any trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->expectFGColor(96, 96);
+        sc->expectBGColor(160, 160);
+    }
+
+    // should trigger the first deferred transaction, but not the second one
+    TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    {
+        SCOPED_TRACE("after first trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->checkPixel(96, 96, 162, 63, 96);
+        sc->expectBGColor(160, 160);
+    }
+
+    // should show up immediately since it's not deferred
+    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); });
+
+    // trigger the second deferred transaction
+    TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
+    {
+        SCOPED_TRACE("after second trigger");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(32, 32);
+        sc->expectBGColor(96, 96);
+        sc->expectFGColor(160, 160);
+    }
+}
+
+TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    sp<SurfaceControl> childNoBuffer =
+            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());
+    TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200);
+    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{}
+            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
+            .apply(true);
+    {
+        ScreenCapture::captureScreen(&sc);
+        sc->expectChildColor(73, 73);
+        sc->expectChildColor(74, 74);
+    }
+}
+
+TEST_F(LayerUpdateTest, MergingTransactions) {
+    std::unique_ptr<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before move");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(0, 12);
+        sc->expectFGColor(75, 75);
+        sc->expectBGColor(145, 145);
+    }
+
+    Transaction t1, t2;
+    t1.setPosition(mFGSurfaceControl, 128, 128);
+    t2.setPosition(mFGSurfaceControl, 0, 0);
+    // We expect that the position update from t2 now
+    // overwrites the position update from t1.
+    t1.merge(std::move(t2));
+    t1.apply();
+
+    {
+        ScreenCapture::captureScreen(&sc);
+        sc->expectFGColor(1, 1);
+    }
+}
+
+TEST_F(LayerUpdateTest, MergingTransactionFlags) {
+    Transaction().hide(mFGSurfaceControl).apply();
+    std::unique_ptr<ScreenCapture> sc;
+    {
+        SCOPED_TRACE("before merge");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectBGColor(0, 12);
+        sc->expectBGColor(75, 75);
+        sc->expectBGColor(145, 145);
+    }
+
+    Transaction t1, t2;
+    t1.show(mFGSurfaceControl);
+    t2.setFlags(mFGSurfaceControl, 0 /* flags */, layer_state_t::eLayerSecure /* mask */);
+    t1.merge(std::move(t2));
+    t1.apply();
+
+    {
+        SCOPED_TRACE("after merge");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectFGColor(75, 75);
+    }
+}
+
+class ChildLayerTest : public LayerUpdateTest {
+protected:
+    void SetUp() override {
+        LayerUpdateTest::SetUp();
+        mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
+        TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+        {
+            SCOPED_TRACE("before anything");
+            mCapture = screenshot();
+            mCapture->expectChildColor(64, 64);
+        }
+    }
+    void TearDown() override {
+        LayerUpdateTest::TearDown();
+        mChild = 0;
+    }
+
+    sp<SurfaceControl> mChild;
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ChildLayerTest, ChildLayerPositioning) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        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);
+    }
+
+    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
+
+    {
+        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
+        mCapture->expectChildColor(10, 10);
+        // And 10 more pixels we should be back to the foreground surface
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerCropping) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(4, 4);
+        mCapture->expectBGColor(5, 5);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerConstraints) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setPosition(mChild, 63, 63);
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(0, 0);
+        // Last pixel in foreground should now be the child.
+        mCapture->expectChildColor(63, 63);
+        // But the child should be constrained and the next pixel
+        // must be the background
+        mCapture->expectBGColor(64, 64);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerScaling) {
+    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
+
+    // Find the boundary between the parent and child
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectFGColor(10, 10);
+    }
+
+    asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); });
+
+    // The boundary should be twice as far from the origin now.
+    // The pixels from the last test should all be child now
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+        mCapture->expectChildColor(19, 19);
+        mCapture->expectFGColor(20, 20);
+    }
+}
+
+// A child with a scale transform should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
+    asTransaction([&](Transaction& t) {
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setPosition(mChild, 0, 0);
+    });
+
+    // Find the boundary between the parent and child.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 9);
+        mCapture->expectFGColor(10, 10);
+    }
+
+    asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
+
+    // The child should fill its parent bounds and be cropped by it.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerAlpha) {
+    TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
+    TransactionUtils::fillSurfaceRGBA8(mChild, 0, 254, 0);
+    waitForPostedBuffers();
+
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        // Unblended child color
+        mCapture->checkPixel(0, 0, 0, 254, 0);
+    }
+
+    asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
+
+    {
+        mCapture = screenshot();
+        // Child and BG blended.
+        mCapture->checkPixel(0, 0, 127, 127, 0);
+    }
+
+    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
+
+    {
+        mCapture = screenshot();
+        // Child and BG blended.
+        mCapture->checkPixel(0, 0, 95, 64, 95);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentChildren) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        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);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        // In reparenting we should have exposed the entire foreground surface.
+        mCapture->expectFGColor(74, 74);
+        // And the child layer should now begin at 10, 10 (since the BG
+        // layer is at (0, 0)).
+        mCapture->expectBGColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) {
+    sp<SurfaceControl> mGrandChild =
+            createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    TransactionUtils::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, ChildrenRelativeZSurvivesParentDestruction) {
+    sp<SurfaceControl> mGrandChild =
+            createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111);
+
+    // draw grand child behind the foreground surface
+    asTransaction([&](Transaction& t) {
+        t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1);
+    });
+
+    {
+        SCOPED_TRACE("Child visible");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 200, 200, 200);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.reparent(mChild, nullptr);
+        t.reparentChildren(mChild, mFGSurfaceControl->getHandle());
+    });
+
+    {
+        SCOPED_TRACE("foreground visible reparenting grandchild");
+        ScreenCapture::captureScreen(&mCapture);
+        mCapture->checkPixel(64, 64, 195, 63, 63);
+    }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenSameClient) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        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);
+    }
+
+    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
+
+    asTransaction([&](Transaction& t) { t.hide(mChild); });
+
+    // Since the child has the same client as the parent, it will not get
+    // detached and will be hidden.
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectFGColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
+    sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
+    sp<SurfaceControl> mChildNewClient =
+            createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
+                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    ASSERT_TRUE(mChildNewClient->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
+
+    asTransaction([&](Transaction& t) {
+        t.hide(mChild);
+        t.show(mChildNewClient);
+        t.setPosition(mChildNewClient, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        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);
+    }
+
+    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
+
+    asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
+
+    // Nothing should have changed.
+    {
+        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());
+
+    TransactionUtils::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, DetachChildrenWithDeferredTransaction) {
+    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());
+
+    TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200);
+
+    Transaction()
+            .hide(mChild)
+            .show(childNewClient)
+            .setPosition(childNewClient, 10, 10)
+            .setPosition(mFGSurfaceControl, 64, 64)
+            .apply();
+
+    {
+        mCapture = screenshot();
+        Rect rect = Rect(74, 74, 84, 84);
+        mCapture->expectBorder(rect, Color{195, 63, 63, 255});
+        mCapture->expectColor(rect, Color{200, 200, 200, 255});
+    }
+
+    Transaction()
+            .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
+                                          mFGSurfaceControl->getSurface()->getNextFrameNumber())
+            .apply();
+    Transaction().detachChildren(mFGSurfaceControl).apply();
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
+
+    // BufferLayer can still dequeue buffers even though there's a detached layer with a
+    // deferred transaction.
+    {
+        SCOPED_TRACE("new buffer");
+        mCapture = screenshot();
+        Rect rect = Rect(74, 74, 84, 84);
+        mCapture->expectBorder(rect, Color::RED);
+        mCapture->expectColor(rect, Color{200, 200, 200, 255});
+    }
+}
+
+TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        // But it's only 10x15.
+        mCapture->expectFGColor(10, 15);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // We cause scaling by 2.
+        t.setSize(mFGSurfaceControl, 128, 128);
+    });
+
+    {
+        mCapture = screenshot();
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(10, 10);
+        mCapture->expectChildColor(19, 29);
+        // And now it should be scaled all the way to 20x30
+        mCapture->expectFGColor(20, 30);
+    }
+}
+
+// Regression test for b/37673612
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        // We've positioned the child in the top left.
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 14);
+        // But it's only 10x15.
+        mCapture->expectFGColor(10, 15);
+    }
+    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+    // the WM specified state size.
+    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    {
+        // The child should still be in the same place and not have any strange scaling as in
+        // b/37673612.
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectFGColor(10, 10);
+    }
+}
+
+// A child with a buffer transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setSize(mChild, 100, 100);
+    });
+    TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+    {
+        mCapture = screenshot();
+
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    // Apply a 90 transform on the buffer.
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 64, 128);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    // The child should be cropped by the new parent bounds.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(99, 63);
+        mCapture->expectFGColor(100, 63);
+        mCapture->expectBGColor(128, 64);
+    }
+}
+
+// A child with a scale transform from its parents should be cropped by its parent bounds.
+TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+        t.setSize(mChild, 200, 200);
+    });
+    TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+    {
+        mCapture = screenshot();
+
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(63, 63);
+        mCapture->expectBGColor(64, 64);
+    }
+
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        // Set a scaling by 2.
+        t.setSize(mFGSurfaceControl, 128, 128);
+    });
+
+    // Child should inherit its parents scale but should be cropped by its parent bounds.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(127, 127);
+        mCapture->expectBGColor(128, 128);
+    }
+}
+
+// Regression test for b/127368943
+// Child should ignore the buffer transform but apply parent scale transform.
+TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 0, 0);
+        t.setPosition(mFGSurfaceControl, 0, 0);
+    });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(9, 14);
+        mCapture->expectFGColor(10, 15);
+    }
+
+    // Change the size of the foreground to 128 * 64 so we can test rotation as well.
+    asTransaction([&](Transaction& t) {
+        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        t.setSize(mFGSurfaceControl, 128, 64);
+    });
+    sp<Surface> s = mFGSurfaceControl->getSurface();
+    auto anw = static_cast<ANativeWindow*>(s.get());
+    // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
+    // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
+    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+    native_window_set_buffers_dimensions(anw, 32, 64);
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
+    waitForPostedBuffers();
+
+    // The child should ignore the buffer transform but apply the 2.0 scale from parent.
+    {
+        mCapture = screenshot();
+        mCapture->expectChildColor(0, 0);
+        mCapture->expectChildColor(19, 29);
+        mCapture->expectFGColor(20, 30);
+    }
+}
+
+TEST_F(ChildLayerTest, Bug36858924) {
+    // Destroy the child layer
+    mChild.clear();
+
+    // Now recreate it as hidden
+    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_legacy(mChild, mFGSurfaceControl->getHandle(),
+                                       mFGSurfaceControl->getSurface()->getNextFrameNumber());
+        t.show(mChild);
+    });
+
+    // Render the foreground surface a few times
+    //
+    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
+    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
+    // never acquire/release the first buffer
+    ALOGI("Filling 1");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+    ALOGI("Filling 2");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
+    ALOGI("Filling 3");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
+    ALOGI("Filling 4");
+    TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
+}
+
+TEST_F(ChildLayerTest, Reparent) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        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);
+    }
+
+    asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
+
+    {
+        mCapture = screenshot();
+        mCapture->expectFGColor(64, 64);
+        // In reparenting we should have exposed the entire foreground surface.
+        mCapture->expectFGColor(74, 74);
+        // And the child layer should now begin at 10, 10 (since the BG
+        // layer is at (0, 0)).
+        mCapture->expectBGColor(9, 9);
+        mCapture->expectChildColor(10, 10);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentToNoParent) {
+    asTransaction([&](Transaction& t) {
+        t.show(mChild);
+        t.setPosition(mChild, 10, 10);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        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);
+    }
+    asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
+    {
+        mCapture = screenshot();
+        // The surface should now be offscreen.
+        mCapture->expectFGColor(64, 64);
+        mCapture->expectFGColor(74, 74);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, ReparentFromNoParent) {
+    sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
+    ASSERT_TRUE(newSurface != nullptr);
+    ASSERT_TRUE(newSurface->isValid());
+
+    TransactionUtils::fillSurfaceRGBA8(newSurface, 63, 195, 63);
+    asTransaction([&](Transaction& t) {
+        t.hide(mChild);
+        t.show(newSurface);
+        t.setPosition(newSurface, 10, 10);
+        t.setLayer(newSurface, INT32_MAX - 2);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+    });
+
+    {
+        mCapture = screenshot();
+        // Top left of foreground must now be visible
+        mCapture->expectFGColor(64, 64);
+        // At 10, 10 we should see the new surface
+        mCapture->checkPixel(10, 10, 63, 195, 63);
+    }
+
+    asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
+
+    {
+        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);
+        mCapture->checkPixel(74, 74, 63, 195, 63);
+        mCapture->expectFGColor(84, 84);
+    }
+}
+
+TEST_F(ChildLayerTest, NestedChildren) {
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+    {
+        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);
+    }
+}
+
+TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
+    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
+    TransactionUtils::fillSurfaceRGBA8(relative, 255, 255, 255);
+
+    Transaction t;
+    t.setLayer(relative, INT32_MAX)
+            .setRelativeLayer(mChild, relative->getHandle(), 1)
+            .setPosition(mFGSurfaceControl, 0, 0)
+            .apply(true);
+
+    // We expect that the child should have been elevated above our
+    // INT_MAX layer even though it's not a child of it.
+    {
+        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::eFXSurfaceEffect, 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::eFXSurfaceEffect, 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::eFXSurfaceEffect, 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::eFXSurfaceEffect, 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::eFXSurfaceEffect, 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::eFXSurfaceEffect, 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:
+    std::unique_ptr<ScreenCapture> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+    auto bgHandle = mBGSurfaceControl->getHandle();
+    ScreenCapture::captureLayers(&mCapture, bgHandle);
+    mCapture->expectBGColor(0, 0);
+    // Doesn't capture FG layer which is at 64, 64
+    mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    // Captures mFGSurfaceControl layer and its child.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    // Captures mFGSurfaceControl's child
+    ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .show(child2)
+            .setLayer(child, 1)
+            .setLayer(child2, 2)
+            .apply(true);
+
+    // Child2 would be visible but its excluded, so we should see child1 color instead.
+    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+// Like the last test but verifies that children are also exclude.
+TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+    sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
+                                              PIXEL_FORMAT_RGBA_8888, 0, child2.get());
+    TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .show(child2)
+            .show(child3)
+            .setLayer(child, 1)
+            .setLayer(child2, 2)
+            .apply(true);
+
+    // Child2 would be visible but its excluded, so we should see child1 color instead.
+    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
+    mCapture->checkPixel(10, 10, 0, 0, 0);
+    mCapture->checkPixel(0, 0, 200, 200, 200);
+}
+
+TEST_F(ScreenCaptureTest, CaptureTransparent) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    auto childHandle = child->getHandle();
+
+    // Captures child
+    ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
+    mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
+    // Area outside of child's bounds is transparent.
+    mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
+}
+
+TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    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);
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            // Set relative layer above fg layer so should be shown above when computing all layers.
+            .setRelativeLayer(relative, fgHandle, 1)
+            .show(relative)
+            .apply(true);
+
+    // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    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());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            // Set relative layer below fg layer but relative to child layer so it should be shown
+            // above child layer.
+            .setLayer(relative, -1)
+            .setRelativeLayer(relative, child->getHandle(), 1)
+            .show(relative)
+            .apply(true);
+
+    // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
+    // relative value should be taken into account, placing it above child layer.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    // Relative layer is showing on top of child layer
+    mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop(0, 0, 10, 10);
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    Rect layerCrop(0, 0, 10, 10);
+    SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+    sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+
+    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    SurfaceComposerClient::Transaction().show(child).apply(true);
+
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    sp<GraphicBuffer> outBuffer;
+    Rect sourceCrop = Rect();
+    ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+
+    TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+    SurfaceComposerClient::Transaction().apply(true);
+    ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+    ScreenCapture sc(outBuffer);
+    sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+// In the following tests we verify successful skipping of a parent layer,
+// so we use the same verification logic and only change how we mutate
+// the parent layer to verify that various properties are ignored.
+class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
+public:
+    void SetUp() override {
+        LayerUpdateTest::SetUp();
+
+        mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0,
+                               mFGSurfaceControl.get());
+        TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200);
+
+        SurfaceComposerClient::Transaction().show(mChild).apply(true);
+    }
+
+    void verify(std::function<void()> verifyStartingState) {
+        // Verify starting state before a screenshot is taken.
+        verifyStartingState();
+
+        // Verify child layer does not inherit any of the properties of its
+        // parent when its screenshot is captured.
+        auto fgHandle = mFGSurfaceControl->getHandle();
+        ScreenCapture::captureChildLayers(&mCapture, fgHandle);
+        mCapture->checkPixel(10, 10, 0, 0, 0);
+        mCapture->expectChildColor(0, 0);
+
+        // Verify all assumptions are still true after the screenshot is taken.
+        verifyStartingState();
+    }
+
+    std::unique_ptr<ScreenCapture> mCapture;
+    sp<SurfaceControl> mChild;
+};
+
+// Regression test b/76099859
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
+    SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
+
+    // Even though the parent is hidden we should still capture the child.
+
+    // Before and after reparenting, verify child is properly hidden
+    // when rendering full-screen.
+    verify([&] { screenshot()->expectBGColor(64, 64); });
+}
+
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
+    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.
+
+    // Before and after reparenting, verify child is cropped by parent.
+    verify([&] { screenshot()->expectBGColor(65, 65); });
+}
+
+// Regression test b/124372894
+TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
+    SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
+
+    // We should not inherit the parent scaling.
+
+    // Before and after reparenting, verify child is properly scaled.
+    verify([&] { screenshot()->expectChildColor(80, 80); });
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+    auto fgHandle = mFGSurfaceControl->getHandle();
+
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .setPosition(grandchild, 5, 5)
+            .show(grandchild)
+            .apply(true);
+
+    // Captures mFGSurfaceControl, its child, and the grandchild.
+    ScreenCapture::captureLayers(&mCapture, fgHandle);
+    mCapture->expectFGColor(10, 10);
+    mCapture->expectChildColor(0, 0);
+    mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    auto childHandle = child->getHandle();
+
+    SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
+
+    // Captures only the child layer, and not the parent.
+    ScreenCapture::captureLayers(&mCapture, childHandle);
+    mCapture->expectChildColor(0, 0);
+    mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+    TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200);
+    auto childHandle = child->getHandle();
+
+    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
+                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
+    TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+    SurfaceComposerClient::Transaction()
+            .show(child)
+            .setPosition(grandchild, 5, 5)
+            .show(grandchild)
+            .apply(true);
+
+    auto grandchildHandle = grandchild->getHandle();
+
+    // Captures only the grandchild.
+    ScreenCapture::captureLayers(&mCapture, grandchildHandle);
+    mCapture->checkPixel(0, 0, 50, 50, 50);
+    mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureCrop) {
+    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(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+    SurfaceComposerClient::Transaction()
+            .setLayer(redLayer, INT32_MAX - 1)
+            .show(redLayer)
+            .show(blueLayer)
+            .apply(true);
+
+    auto redLayerHandle = redLayer->getHandle();
+
+    // Capturing full screen should have both red and blue are visible.
+    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+    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.
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureSize) {
+    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(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
+
+    SurfaceComposerClient::Transaction()
+            .setLayer(redLayer, INT32_MAX - 1)
+            .show(redLayer)
+            .show(blueLayer)
+            .apply(true);
+
+    auto redLayerHandle = redLayer->getHandle();
+
+    // Capturing full screen should have both red and blue are visible.
+    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
+    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
+
+    ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
+    // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
+    mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
+    // red area below the blue area
+    mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
+    // red area to the right of the blue area
+    mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
+    mCapture->checkPixel(30, 30, 0, 0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
+    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
+
+    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
+
+    auto redLayerHandle = redLayer->getHandle();
+    redLayer.clear();
+    SurfaceComposerClient::Transaction().apply(true);
+
+    sp<GraphicBuffer> outBuffer;
+
+    // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
+    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+    ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
new file mode 100644
index 0000000..b49bd54
--- /dev/null
+++ b/services/surfaceflinger/tests/MirrorLayer_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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class MirrorLayerTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        mParentLayer = createColorLayer("Parent layer", Color::RED);
+        mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get());
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+            t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer);
+            t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer);
+            t.setPosition(mChildLayer, 50, 50);
+            t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+            t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mParentLayer = 0;
+        mChildLayer = 0;
+    }
+
+    sp<SurfaceControl> mParentLayer;
+    sp<SurfaceControl> mChildLayer;
+};
+
+TEST_F(MirrorLayerTest, MirrorColorLayer) {
+    sp<SurfaceControl> grandchild =
+            createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get());
+    Transaction()
+            .setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
+            .setCrop_legacy(grandchild, Rect(0, 0, 200, 200))
+            .show(grandchild)
+            .apply();
+
+    // Mirror mChildLayer
+    sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+    ASSERT_NE(mirrorLayer, nullptr);
+
+    // Add mirrorLayer as child of mParentLayer so it's shown on the display
+    Transaction()
+            .reparent(mirrorLayer, mParentLayer->getHandle())
+            .setPosition(mirrorLayer, 500, 500)
+            .show(mirrorLayer)
+            .apply();
+
+    {
+        SCOPED_TRACE("Initial Mirror");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    // Set color to white on grandchild layer.
+    Transaction().setColor(grandchild, half3{1, 1, 1}).apply();
+    {
+        SCOPED_TRACE("Updated Grandchild Layer Color");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    // Set color to black on child layer.
+    Transaction().setColor(mChildLayer, half3{0, 0, 0}).apply();
+    {
+        SCOPED_TRACE("Updated Child Layer Color");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+    }
+
+    // Remove grandchild layer
+    Transaction().reparent(grandchild, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed Grandchild Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+    }
+
+    // Remove child layer
+    Transaction().reparent(mChildLayer, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed Child Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+    }
+
+    // Add grandchild layer to offscreen layer
+    Transaction().reparent(grandchild, mChildLayer->getHandle()).apply();
+    {
+        SCOPED_TRACE("Added Grandchild Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::RED);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::RED);
+    }
+
+    // Add child layer
+    Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply();
+    {
+        SCOPED_TRACE("Added Child Layer");
+        auto shot = screenshot();
+        // Grandchild mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK);
+    }
+}
+
+TEST_F(MirrorLayerTest, MirrorBufferLayer) {
+    sp<SurfaceControl> bufferQueueLayer =
+            createLayer("BufferQueueLayer", 200, 200, 0, mChildLayer.get());
+    fillBufferQueueLayerColor(bufferQueueLayer, Color::BLUE, 200, 200);
+    Transaction().show(bufferQueueLayer).apply();
+
+    sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+    Transaction()
+            .reparent(mirrorLayer, mParentLayer->getHandle())
+            .setPosition(mirrorLayer, 500, 500)
+            .show(mirrorLayer)
+            .apply();
+
+    {
+        SCOPED_TRACE("Initial Mirror BufferQueueLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    fillBufferQueueLayerColor(bufferQueueLayer, Color::WHITE, 200, 200);
+    {
+        SCOPED_TRACE("Update BufferQueueLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    Transaction().reparent(bufferQueueLayer, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed BufferQueueLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    sp<SurfaceControl> bufferStateLayer =
+            createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState,
+                        mChildLayer.get());
+    fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200);
+    Transaction().setFrame(bufferStateLayer, Rect(0, 0, 200, 200)).show(bufferStateLayer).apply();
+
+    {
+        SCOPED_TRACE("Initial Mirror BufferStateLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200);
+    {
+        SCOPED_TRACE("Update BufferStateLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+
+    Transaction().reparent(bufferStateLayer, nullptr).apply();
+    {
+        SCOPED_TRACE("Removed BufferStateLayer");
+        auto shot = screenshot();
+        // Buffer mirror
+        shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN);
+        // Child mirror
+        shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
new file mode 100644
index 0000000..f8a5b40
--- /dev/null
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <ui/DisplayState.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+        SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
+        SurfaceComposerClient::getActiveDisplayConfig(mMainDisplay, &mMainDisplayConfig);
+
+        sp<IGraphicBufferConsumer> consumer;
+        BufferQueue::createBufferQueue(&mProducer, &consumer);
+        consumer->setConsumerName(String8("Virtual disp consumer"));
+        consumer->setDefaultBufferSize(mMainDisplayConfig.resolution.getWidth(),
+                                       mMainDisplayConfig.resolution.getHeight());
+    }
+
+    virtual void TearDown() {
+        SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+        LayerTransactionTest::TearDown();
+        mColorLayer = 0;
+    }
+
+    void createDisplay(const ui::Size& layerStackSize, uint32_t layerStack) {
+        mVirtualDisplay =
+                SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+        asTransaction([&](Transaction& t) {
+            t.setDisplaySurface(mVirtualDisplay, mProducer);
+            t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+            t.setDisplayProjection(mVirtualDisplay, mMainDisplayState.orientation,
+                                   Rect(layerStackSize), Rect(mMainDisplayConfig.resolution));
+        });
+    }
+
+    void createColorLayer(uint32_t layerStack) {
+        mColorLayer =
+                createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
+                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect);
+        ASSERT_TRUE(mColorLayer != nullptr);
+        ASSERT_TRUE(mColorLayer->isValid());
+        asTransaction([&](Transaction& t) {
+            t.setLayerStack(mColorLayer, layerStack);
+            t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+            t.setLayer(mColorLayer, INT32_MAX - 2);
+            t.setColor(mColorLayer,
+                       half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
+                             mExpectedColor.b / 255.0f});
+            t.show(mColorLayer);
+        });
+    }
+
+    ui::DisplayState mMainDisplayState;
+    DisplayConfig mMainDisplayConfig;
+    sp<IBinder> mMainDisplay;
+    sp<IBinder> mVirtualDisplay;
+    sp<IGraphicBufferProducer> mProducer;
+    sp<SurfaceControl> mColorLayer;
+    Color mExpectedColor = {63, 63, 195, 255};
+};
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+    createDisplay(mMainDisplayState.viewport, 1 /* layerStack */);
+    createColorLayer(1 /* layerStack */);
+
+    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+    // Verify color layer does not render on main display.
+    std::unique_ptr<ScreenCapture> sc;
+    ScreenCapture::captureScreen(&sc, mMainDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+    // Verify color layer renders correctly on virtual display.
+    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
+}
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+    // Create a display and set its layer stack to the main display's layer stack so
+    // the contents of the main display are mirrored on to the virtual display.
+
+    // Assumption here is that the new mirrored display has the same viewport as the
+    // primary display that it is mirroring.
+    createDisplay(mMainDisplayState.viewport, 0 /* layerStack */);
+    createColorLayer(0 /* layerStack */);
+
+    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+    // Verify color layer renders correctly on main display and it is mirrored on the
+    // virtual display.
+    std::unique_ptr<ScreenCapture> sc;
+    ScreenCapture::captureScreen(&sc, mMainDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp
new file mode 100644
index 0000000..1180cac
--- /dev/null
+++ b/services/surfaceflinger/tests/RelativeZ_test.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+::testing::Environment* const binderEnv =
+        ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
+
+class RelativeZTest : public LayerTransactionTest {
+protected:
+    virtual void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        const auto display = SurfaceComposerClient::getInternalDisplayToken();
+        ASSERT_FALSE(display == nullptr);
+
+        // Back layer
+        mBackgroundLayer = createColorLayer("Background layer", Color::RED);
+
+        // Front layer
+        mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
+
+        asTransaction([&](Transaction& t) {
+            t.setDisplayLayerStack(display, 0);
+            t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
+            t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
+        });
+    }
+
+    virtual void TearDown() {
+        LayerTransactionTest::TearDown();
+        mBackgroundLayer = 0;
+        mForegroundLayer = 0;
+    }
+
+    sp<SurfaceControl> mBackgroundLayer;
+    sp<SurfaceControl> mForegroundLayer;
+};
+
+// When a layer is reparented offscreen, remove relative z order if the relative parent
+// is still onscreen so that the layer is not drawn.
+TEST_F(RelativeZTest, LayerRemoved) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    // Background layer (RED)
+    //   Child layer (WHITE) (relative to foregroud layer)
+    // Foregroud layer (GREEN)
+    sp<SurfaceControl> childLayer =
+            createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
+
+    Transaction{}
+            .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
+            .show(childLayer)
+            .apply();
+
+    {
+        // The childLayer should be in front of the FG control.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLayer, nullptr).apply();
+
+    // Background layer (RED)
+    //   Child layer (WHITE)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
+
+    {
+        // The relative z info for child layer should be reset, leaving FG control on top.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+}
+
+// When a layer is reparented offscreen, preseve relative z order if the relative parent
+// is also offscreen. Regression test b/132613412
+TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1 (WHITE)
+    //     child level 2a (BLUE)
+    //       child level 3 (GREEN) (relative to child level 2b)
+    //     child level 2b (BLACK)
+    sp<SurfaceControl> childLevel1 =
+            createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get());
+    sp<SurfaceControl> childLevel2a =
+            createColorLayer("child level 2a", Color::BLUE, childLevel1.get());
+    sp<SurfaceControl> childLevel2b =
+            createColorLayer("child level 2b", Color::BLACK, childLevel1.get());
+    sp<SurfaceControl> childLevel3 =
+            createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
+
+    Transaction{}
+            .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
+            .show(childLevel2a)
+            .show(childLevel2b)
+            .show(childLevel3)
+            .apply();
+
+    {
+        // The childLevel3 should be in front of childLevel2b.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLevel1, nullptr).apply();
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   child level 1 (WHITE)
+    //     child level 2 back (BLUE)
+    //       child level 3 (GREEN) (relative to child level 2b)
+    //     child level 2 front (BLACK)
+    Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
+
+    {
+        // Nothing should change at this point since relative z info was preserved.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+}
+
+TEST_F(RelativeZTest, LayerAndRelativeRemoved) {
+    std::unique_ptr<ScreenCapture> sc;
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   Child layer (BLUE) (relative to relativeToLayer layer)
+    //   Relative layer (WHITE)
+    sp<SurfaceControl> childLayer =
+            createColorLayer("Child layer", Color::BLUE, mForegroundLayer.get());
+    sp<SurfaceControl> relativeToLayer =
+            createColorLayer("Relative layer", Color::WHITE, mForegroundLayer.get());
+
+    Transaction{}
+            .setRelativeLayer(childLayer, relativeToLayer->getHandle(), 1)
+            .show(childLayer)
+            .show(relativeToLayer)
+            .apply();
+
+    {
+        // The childLayer should be in front of relativeToLayer.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+    }
+
+    // Remove layer that childLayer is relative to
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   Child layer (BLUE) (relative to relativeToLayer layer)
+    Transaction{}.reparent(relativeToLayer, nullptr).apply();
+    relativeToLayer = 0;
+
+    {
+        // The child layer is relative to an deleted layer so it won't be drawn.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    Transaction{}.reparent(childLayer, nullptr).apply();
+
+    {
+        // The child layer is offscreen, so it won't be drawn.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
+    }
+
+    // Background layer (RED)
+    // Foregroud layer (GREEN)
+    //   Child layer (BLUE)
+    Transaction{}.reparent(childLayer, mForegroundLayer->getHandle()).apply();
+
+    {
+        // The relative z info for child layer should be reset, leaving the child layer on top.
+        ScreenCapture::captureScreen(&sc);
+        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
+    }
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/SetFrameRate_test.cpp b/services/surfaceflinger/tests/SetFrameRate_test.cpp
new file mode 100644
index 0000000..fc65263
--- /dev/null
+++ b/services/surfaceflinger/tests/SetFrameRate_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include <system/window.h>
+
+#include <thread>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class SetFrameRateTest : public LayerTransactionTest {
+protected:
+    void TearDown() {
+        mLayer = nullptr;
+        LayerTransactionTest::TearDown();
+    }
+
+    void CreateLayer(uint32_t layerType) {
+        ASSERT_EQ(nullptr, mLayer.get());
+        mLayerType = layerType;
+        ASSERT_NO_FATAL_FAILURE(mLayer = createLayer("TestLayer", mLayerWidth, mLayerHeight,
+                                                     /*flags=*/mLayerType));
+        ASSERT_NE(nullptr, mLayer.get());
+    }
+
+    void PostBuffers(const Color& color) {
+        auto startTime = systemTime();
+        while (systemTime() - startTime < s2ns(1)) {
+            ASSERT_NO_FATAL_FAILURE(
+                    fillLayerColor(mLayerType, mLayer, color, mLayerWidth, mLayerHeight));
+            std::this_thread::sleep_for(100ms);
+        }
+    }
+
+    const int mLayerWidth = 32;
+    const int mLayerHeight = 32;
+    sp<SurfaceControl> mLayer;
+    uint32_t mLayerType;
+};
+
+TEST_F(SetFrameRateTest, BufferQueueLayerSetFrameRate) {
+    CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferQueue);
+    native_window_set_frame_rate(mLayer->getSurface().get(), 100.f);
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
+    Transaction().setFrameRate(mLayer, 200.f).apply();
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
+    native_window_set_frame_rate(mLayer->getSurface().get(), 300.f);
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
+}
+
+TEST_F(SetFrameRateTest, BufferStateLayerSetFrameRate) {
+    CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferState);
+    Transaction().setFrameRate(mLayer, 400.f).apply();
+    ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::GREEN));
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/SetGeometry_test.cpp b/services/surfaceflinger/tests/SetGeometry_test.cpp
new file mode 100644
index 0000000..5fe2513
--- /dev/null
+++ b/services/surfaceflinger/tests/SetGeometry_test.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+class SetGeometryTest : public LayerTransactionTest {
+protected:
+    void SetUp() {
+        LayerTransactionTest::SetUp();
+        ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+        mLayer = createLayer("Layer", mLayerWidth, mLayerHeight);
+        fillBufferQueueLayerColor(mLayer, Color::RED, mLayerWidth, mLayerHeight);
+        asTransaction([&](Transaction& t) { t.setLayer(mLayer, INT32_MAX - 1).show(mLayer); });
+
+        {
+            SCOPED_TRACE("init");
+            ScreenCapture::captureScreen(&sc);
+            sc->expectColor(Rect(0, 0, mLayerWidth, mLayerHeight), Color::RED);
+            sc->expectBorder(Rect(0, 0, mLayerWidth, mLayerHeight), Color::BLACK);
+        }
+    }
+
+    void TearDown() {
+        LayerTransactionTest::TearDown();
+        sc = 0;
+        mLayer = 0;
+    }
+
+    std::unique_ptr<ScreenCapture> sc;
+    sp<SurfaceControl> mLayer;
+    const int mLayerWidth = 100;
+    const int mLayerHeight = 200;
+};
+
+TEST_F(SetGeometryTest, SourceAtZeroNoScale) {
+    Rect source = Rect(0, 0, 30, 30);
+    Rect dest = Rect(60, 60, 90, 90);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+    {
+        SCOPED_TRACE("geometry applied");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+}
+
+TEST_F(SetGeometryTest, SourceNotAtZero) {
+    Rect source = Rect(40, 40, 70, 70);
+    Rect dest = Rect(60, 60, 90, 90);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+    {
+        SCOPED_TRACE("geometry applied");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+}
+
+TEST_F(SetGeometryTest, Scale) {
+    Rect source = Rect(0, 0, 100, 200);
+    Rect dest = Rect(0, 0, 200, 400);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+
+    {
+        SCOPED_TRACE("Scaled by 2");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+
+    dest = Rect(0, 0, 50, 100);
+    Transaction{}.setGeometry(mLayer, source, dest, 0).apply();
+    {
+        SCOPED_TRACE("Scaled by .5");
+        ScreenCapture::captureScreen(&sc);
+        sc->expectColor(dest, Color::RED);
+        sc->expectBorder(dest, Color::BLACK);
+    }
+}
+
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp
index ee857b0..e9b6ba0 100644
--- a/services/surfaceflinger/tests/Stress_test.cpp
+++ b/services/surfaceflinger/tests/Stress_test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <gtest/gtest.h>
 
 #include <gui/SurfaceComposerClient.h>
@@ -107,3 +111,6 @@
 }
 
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 6b4634a..cf7d570 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
 {
         "presubmit": {
-            "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*"
+            "filter": ""
         }
 }
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 5cc946a..8d97f27 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <frameworks/native/cmds/surfacereplayer/proto/src/trace.pb.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
-
 #include <gtest/gtest.h>
-
-#include <android/native_window.h>
-
 #include <gui/ISurfaceComposer.h>
 #include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 
 #include <fstream>
 #include <random>
@@ -36,6 +35,9 @@
 namespace android {
 
 using Transaction = SurfaceComposerClient::Transaction;
+using SurfaceChange = surfaceflinger::SurfaceChange;
+using Trace = surfaceflinger::Trace;
+using Increment = surfaceflinger::Increment;
 
 constexpr int32_t SCALING_UPDATE = 1;
 constexpr uint32_t BUFFER_UPDATES = 18;
@@ -43,18 +45,23 @@
 constexpr uint32_t SIZE_UPDATE = 134;
 constexpr uint32_t STACK_UPDATE = 1;
 constexpr uint64_t DEFERRED_UPDATE = 0;
+constexpr int32_t RELATIVE_Z = 42;
 constexpr float ALPHA_UPDATE = 0.29f;
 constexpr float CORNER_RADIUS_UPDATE = 0.2f;
+constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24;
 constexpr float POSITION_UPDATE = 121;
 const Rect CROP_UPDATE(16, 16, 32, 32);
+const float SHADOW_RADIUS_UPDATE = 35.0f;
 
 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 TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
+constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface";
+constexpr auto UNIQUE_TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface#0";
+constexpr auto UNIQUE_TEST_FG_SURFACE_NAME = "FG 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";
+constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb";
 
 // Fill an RGBA_8888 formatted surface with a single color.
 static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) {
@@ -136,12 +143,16 @@
     void TearDown() override {
         mComposerClient->dispose();
         mBGSurfaceControl.clear();
+        mFGSurfaceControl.clear();
         mComposerClient.clear();
+        system("setenforce 1");
     }
 
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mBGSurfaceControl;
+    sp<SurfaceControl> mFGSurfaceControl;
     int32_t mBGLayerId;
+    int32_t mFGLayerId;
 
 public:
     using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&);
@@ -169,6 +180,8 @@
     bool layerUpdateFound(const SurfaceChange& change, bool foundLayer);
     bool cropUpdateFound(const SurfaceChange& change, bool foundCrop);
     bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius);
+    bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
+                                         bool foundBackgroundBlurRadius);
     bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix);
     bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode);
     bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion);
@@ -177,6 +190,11 @@
     bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag);
     bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag);
     bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred);
+    bool reparentUpdateFound(const SurfaceChange& change, bool found);
+    bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
+    bool detachChildrenUpdateFound(const SurfaceChange& change, bool found);
+    bool reparentChildrenUpdateFound(const SurfaceChange& change, bool found);
+    bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
     bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
 
     // Find all of the updates in the single trace
@@ -201,6 +219,7 @@
     void layerUpdate(Transaction&);
     void cropUpdate(Transaction&);
     void cornerRadiusUpdate(Transaction&);
+    void backgroundBlurRadiusUpdate(Transaction&);
     void matrixUpdate(Transaction&);
     void overrideScalingModeUpdate(Transaction&);
     void transparentRegionHintUpdate(Transaction&);
@@ -209,6 +228,11 @@
     void opaqueFlagUpdate(Transaction&);
     void secureFlagUpdate(Transaction&);
     void deferredTransactionUpdate(Transaction&);
+    void reparentUpdate(Transaction&);
+    void relativeParentUpdate(Transaction&);
+    void detachChildrenUpdate(Transaction&);
+    void reparentChildrenUpdate(Transaction&);
+    void shadowRadiusUpdate(Transaction&);
     void surfaceCreation(Transaction&);
     void displayCreation(Transaction&);
     void displayDeletion(Transaction&);
@@ -243,28 +267,37 @@
     const auto display = SurfaceComposerClient::getInternalDisplayToken();
     ASSERT_FALSE(display == nullptr);
 
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-    ssize_t displayWidth = info.w;
-    ssize_t displayHeight = info.h;
+    DisplayConfig config;
+    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+    const ui::Size& resolution = config.resolution;
 
     // Background surface
-    mBGSurfaceControl = mComposerClient->createSurface(
-            String8(TEST_SURFACE_NAME), displayWidth, displayHeight,
-            PIXEL_FORMAT_RGBA_8888, 0);
+    mBGSurfaceControl =
+            mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), resolution.getWidth(),
+                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
     ASSERT_TRUE(mBGSurfaceControl != nullptr);
     ASSERT_TRUE(mBGSurfaceControl->isValid());
 
+    // Foreground surface
+    mFGSurfaceControl =
+            mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), resolution.getWidth(),
+                                           resolution.getHeight(), PIXEL_FORMAT_RGBA_8888, 0);
+    ASSERT_TRUE(mFGSurfaceControl != nullptr);
+    ASSERT_TRUE(mFGSurfaceControl->isValid());
+
     Transaction t;
     t.setDisplayLayerStack(display, 0);
-    ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3)
-            .show(mBGSurfaceControl)
-            .apply());
+    ASSERT_EQ(NO_ERROR,
+              t.setLayer(mBGSurfaceControl, INT_MAX - 3)
+                      .show(mBGSurfaceControl)
+                      .setLayer(mFGSurfaceControl, INT_MAX - 3)
+                      .show(mFGSurfaceControl)
+                      .apply());
 }
 
 void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) {
-    mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME);
+    mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_BG_SURFACE_NAME);
+    mFGLayerId = getSurfaceId(trace, UNIQUE_TEST_FG_SURFACE_NAME);
 }
 
 void SurfaceInterceptorTest::captureTest(TestTransactionAction action,
@@ -322,6 +355,10 @@
     t.setCornerRadius(mBGSurfaceControl, CORNER_RADIUS_UPDATE);
 }
 
+void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) {
+    t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE);
+}
+
 void SurfaceInterceptorTest::layerUpdate(Transaction& t) {
     t.setLayer(mBGSurfaceControl, LAYER_UPDATE);
 }
@@ -364,6 +401,26 @@
                                    DEFERRED_UPDATE);
 }
 
+void SurfaceInterceptorTest::reparentUpdate(Transaction& t) {
+    t.reparent(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+}
+
+void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) {
+    t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl->getHandle(), RELATIVE_Z);
+}
+
+void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) {
+    t.detachChildren(mBGSurfaceControl);
+}
+
+void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) {
+    t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle());
+}
+
+void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
+    t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
+}
+
 void SurfaceInterceptorTest::displayCreation(Transaction&) {
     sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
     SurfaceComposerClient::destroyDisplay(testDisplay);
@@ -379,6 +436,7 @@
     runInTransaction(&SurfaceInterceptorTest::sizeUpdate);
     runInTransaction(&SurfaceInterceptorTest::alphaUpdate);
     runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate);
+    runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate);
     runInTransaction(&SurfaceInterceptorTest::layerUpdate);
     runInTransaction(&SurfaceInterceptorTest::cropUpdate);
     runInTransaction(&SurfaceInterceptorTest::matrixUpdate);
@@ -389,6 +447,11 @@
     runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate);
     runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate);
     runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate);
+    runInTransaction(&SurfaceInterceptorTest::reparentUpdate);
+    runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
+    runInTransaction(&SurfaceInterceptorTest::detachChildrenUpdate);
+    runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
+    runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
 }
 
 void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
@@ -451,6 +514,18 @@
     return foundCornerRadius;
 }
 
+bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange& change,
+                                                             bool foundBackgroundBlur) {
+    bool hasBackgroundBlur(change.background_blur_radius().background_blur_radius() ==
+                           BACKGROUND_BLUR_RADIUS_UPDATE);
+    if (hasBackgroundBlur && !foundBackgroundBlur) {
+        foundBackgroundBlur = true;
+    } else if (hasBackgroundBlur && foundBackgroundBlur) {
+        []() { FAIL(); }();
+    }
+    return foundBackgroundBlur;
+}
+
 bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) {
     bool hasLayer(change.layer().layer() == LAYER_UPDATE);
     if (hasLayer && !foundLayer) {
@@ -569,6 +644,57 @@
     return foundDeferred;
 }
 
+bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) {
+    bool hasId(change.reparent().parent_id() == mFGLayerId);
+    if (hasId && !found) {
+        found = true;
+    } else if (hasId && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) {
+    bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId);
+    if (hasId && !found) {
+        found = true;
+    } else if (hasId && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::detachChildrenUpdateFound(const SurfaceChange& change, bool found) {
+    bool detachChildren(change.detach_children().detach_children());
+    if (detachChildren && !found) {
+        found = true;
+    } else if (detachChildren && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::reparentChildrenUpdateFound(const SurfaceChange& change, bool found) {
+    bool hasId(change.reparent_children().parent_id() == mFGLayerId);
+    if (hasId && !found) {
+        found = true;
+    } else if (hasId && found) {
+        []() { FAIL(); }();
+    }
+    return found;
+}
+
+bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
+                                                     bool foundShadowRadius) {
+    bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
+    if (hasShadowRadius && !foundShadowRadius) {
+        foundShadowRadius = true;
+    } else if (hasShadowRadius && foundShadowRadius) {
+        []() { FAIL(); }();
+    }
+    return foundShadowRadius;
+}
+
 bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
         SurfaceChange::SurfaceChangeCase changeCase) {
     bool foundUpdate = false;
@@ -596,6 +722,9 @@
                         case SurfaceChange::SurfaceChangeCase::kCornerRadius:
                             foundUpdate = cornerRadiusUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius:
+                            foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::kMatrix:
                             foundUpdate = matrixUpdateFound(change, foundUpdate);
                             break;
@@ -620,6 +749,21 @@
                         case SurfaceChange::SurfaceChangeCase::kDeferredTransaction:
                             foundUpdate = deferredTransactionUpdateFound(change, foundUpdate);
                             break;
+                        case SurfaceChange::SurfaceChangeCase::kReparent:
+                            foundUpdate = reparentUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kReparentChildren:
+                            foundUpdate = reparentChildrenUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kRelativeParent:
+                            foundUpdate = relativeParentUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kDetachChildren:
+                            foundUpdate = detachChildrenUpdateFound(change, foundUpdate);
+                            break;
+                        case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+                            foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
+                            break;
                         case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
                             break;
                     }
@@ -644,6 +788,10 @@
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag));
     ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparentChildren));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent));
+    ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDetachChildren));
 }
 
 bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
@@ -759,6 +907,11 @@
             SurfaceChange::SurfaceChangeCase::kCornerRadius);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate,
+                SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) {
     captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix);
 }
@@ -798,6 +951,31 @@
             SurfaceChange::SurfaceChangeCase::kDeferredTransaction);
 }
 
+TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::reparentUpdate,
+                SurfaceChange::SurfaceChangeCase::kReparent);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptReparentChildrenUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::reparentChildrenUpdate,
+                SurfaceChange::SurfaceChangeCase::kReparentChildren);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::relativeParentUpdate,
+                SurfaceChange::SurfaceChangeCase::kRelativeParent);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptDetachChildrenUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::detachChildrenUpdate,
+                SurfaceChange::SurfaceChangeCase::kDetachChildren);
+}
+
+TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
+    captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
+                SurfaceChange::SurfaceChangeCase::kShadowRadius);
+}
+
 TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
     captureTest(&SurfaceInterceptorTest::runAllUpdates,
                 &SurfaceInterceptorTest::assertAllUpdatesFound);
@@ -861,5 +1039,7 @@
     ASSERT_TRUE(bufferUpdatesFound(capturedTrace));
     ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation));
 }
-
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
new file mode 100644
index 0000000..040852f
--- /dev/null
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -0,0 +1,119 @@
+/*
+ * 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_TRANSACTION_TEST_HARNESSES
+#define ANDROID_TRANSACTION_TEST_HARNESSES
+
+#include <ui/DisplayState.h>
+
+#include "LayerTransactionTest.h"
+
+namespace android {
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+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:
+
+                const auto displayToken = SurfaceComposerClient::getInternalDisplayToken();
+
+                ui::DisplayState displayState;
+                SurfaceComposerClient::getDisplayState(displayToken, &displayState);
+
+                DisplayConfig displayConfig;
+                SurfaceComposerClient::getActiveDisplayConfig(displayToken, &displayConfig);
+                const ui::Size& resolution = displayConfig.resolution;
+
+                sp<IBinder> vDisplay;
+                sp<IGraphicBufferProducer> producer;
+                sp<IGraphicBufferConsumer> consumer;
+                sp<BufferItemConsumer> itemConsumer;
+                BufferQueue::createBufferQueue(&producer, &consumer);
+
+                consumer->setConsumerName(String8("Virtual disp consumer"));
+                consumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
+
+                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, displayState.orientation,
+                                       Rect(displayState.viewport), Rect(resolution));
+                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;
+};
+
+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,
+                                   uint32_t* outTransformHint = 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,
+                                                 outTransformHint);
+    }
+
+    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;
+};
+} // namespace android
+#endif
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
deleted file mode 100644
index c93e15e..0000000
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ /dev/null
@@ -1,6156 +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.
- */
-
-#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/IProducerListener.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <gui/SurfaceComposerClient.h>
-#include <hardware/hwcomposer_defs.h>
-#include <private/android_filesystem_config.h>
-#include <private/gui/ComposerService.h>
-
-#include <ui/ColorSpace.h>
-#include <ui/DisplayInfo.h>
-#include <ui/Rect.h>
-#include <utils/String8.h>
-
-#include <math.h>
-#include <math/vec3.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "BufferGenerator.h"
-
-namespace android {
-
-namespace {
-
-struct Color {
-    uint8_t r;
-    uint8_t g;
-    uint8_t b;
-    uint8_t a;
-
-    static const Color RED;
-    static const Color GREEN;
-    static const Color BLUE;
-    static const Color WHITE;
-    static const Color BLACK;
-    static const Color TRANSPARENT;
-};
-
-const Color Color::RED{255, 0, 0, 255};
-const Color Color::GREEN{0, 255, 0, 255};
-const Color Color::BLUE{0, 0, 255, 255};
-const Color Color::WHITE{255, 255, 255, 255};
-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 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;
-    }
-
-    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;
-            dst[3] = color.a;
-            dst += 4;
-        }
-    }
-}
-
-// 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) {
-    int32_t x = rect.left;
-    int32_t y = rect.top;
-    int32_t width = rect.right - rect.left;
-    int32_t height = rect.bottom - rect.top;
-
-    int32_t bufferWidth = int32_t(outBuffer->getWidth());
-    int32_t bufferHeight = int32_t(outBuffer->getHeight());
-    if (x + width > bufferWidth) {
-        x = std::min(x, bufferWidth);
-        width = bufferWidth - x;
-    }
-    if (y + height > bufferHeight) {
-        y = std::min(y, bufferHeight);
-        height = bufferHeight - y;
-    }
-
-    auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
-        uint8_t tmp = a >= b ? a - b : b - a;
-        return tmp <= tolerance;
-    };
-    for (int32_t j = 0; j < height; j++) {
-        const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
-        for (int32_t i = 0; i < width; i++) {
-            const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
-            EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
-                    << "pixel @ (" << x + i << ", " << y + j << "): "
-                    << "expected (" << color << "), "
-                    << "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
-            src += 4;
-        }
-    }
-}
-
-} // anonymous namespace
-
-using Transaction = SurfaceComposerClient::Transaction;
-
-// Fill an RGBA_8888 formatted surface with a single color.
-static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
-                             bool unlock = true) {
-    ANativeWindow_Buffer outBuffer;
-    sp<Surface> s = sc->getSurface();
-    ASSERT_TRUE(s != nullptr);
-    ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
-    uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
-    for (int y = 0; y < outBuffer.height; y++) {
-        for (int x = 0; x < outBuffer.width; x++) {
-            uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
-            pixel[0] = r;
-            pixel[1] = g;
-            pixel[2] = b;
-            pixel[3] = 255;
-        }
-    }
-    if (unlock) {
-        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-    }
-}
-
-// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
-// individual pixel values for testing purposes.
-class ScreenCapture : public RefBase {
-public:
-    static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
-        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
-    }
-
-    static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
-        const auto sf = ComposerService::getComposerService();
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-                              Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-                                   Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    static void captureChildLayersExcluding(
-            std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
-            std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        SurfaceComposerClient::Transaction().apply(true);
-
-        sp<GraphicBuffer> outBuffer;
-        ASSERT_EQ(NO_ERROR,
-                  sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
-                                    ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
-                                    1.0f, true));
-        *sc = std::make_unique<ScreenCapture>(outBuffer);
-    }
-
-    void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
-        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
-        expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
-    }
-
-    void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
-        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
-        const bool leftBorder = rect.left > 0;
-        const bool topBorder = rect.top > 0;
-        const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth());
-        const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight());
-
-        if (topBorder) {
-            Rect top(rect.left, rect.top - 1, rect.right, rect.top);
-            if (leftBorder) {
-                top.left -= 1;
-            }
-            if (rightBorder) {
-                top.right += 1;
-            }
-            expectColor(top, color, tolerance);
-        }
-        if (leftBorder) {
-            Rect left(rect.left - 1, rect.top, rect.left, rect.bottom);
-            expectColor(left, color, tolerance);
-        }
-        if (rightBorder) {
-            Rect right(rect.right, rect.top, rect.right + 1, rect.bottom);
-            expectColor(right, color, tolerance);
-        }
-        if (bottomBorder) {
-            Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1);
-            if (leftBorder) {
-                bottom.left -= 1;
-            }
-            if (rightBorder) {
-                bottom.right += 1;
-            }
-            expectColor(bottom, color, tolerance);
-        }
-    }
-
-    void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight,
-                        const Color& bottomLeft, const Color& bottomRight, bool filtered = false,
-                        uint8_t tolerance = 0) {
-        ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0);
-
-        const int32_t centerX = rect.left + (rect.right - rect.left) / 2;
-        const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2;
-        // avoid checking borders due to unspecified filtering behavior
-        const int32_t offsetX = filtered ? 2 : 0;
-        const int32_t offsetY = filtered ? 2 : 0;
-        expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft,
-                    tolerance);
-        expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight,
-                    tolerance);
-        expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft,
-                    tolerance);
-        expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom),
-                    bottomRight, tolerance);
-    }
-
-    void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
-        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
-        const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
-        if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
-            String8 err(String8::format("pixel @ (%3d, %3d): "
-                                        "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
-                                        x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
-            EXPECT_EQ(String8(), err) << err.string();
-        }
-    }
-
-    void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
-
-    void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
-
-    void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
-
-    explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
-        mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
-    }
-
-    ~ScreenCapture() { mOutBuffer->unlock(); }
-
-private:
-    sp<GraphicBuffer> mOutBuffer;
-    uint8_t* mPixels = nullptr;
-};
-
-class LayerTransactionTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        mClient = new SurfaceComposerClient;
-        ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient";
-
-        ASSERT_NO_FATAL_FAILURE(SetUpDisplay());
-
-        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-        ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed));
-    }
-
-    virtual void TearDown() {
-        mBlackBgSurface = 0;
-        mClient->dispose();
-        mClient = 0;
-    }
-
-    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();
-        }
-
-        return 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);
-    }
-
-    sp<SurfaceControl> createColorLayer(const char* name, const Color& color,
-                                        SurfaceControl* parent = nullptr) {
-        auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */,
-                                        PIXEL_FORMAT_RGBA_8888,
-                                        ISurfaceComposerClient::eFXSurfaceColor, parent);
-        asTransaction([&](Transaction& t) {
-            t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f});
-            t.setAlpha(colorLayer, color.a / 255.0f);
-        });
-        return colorLayer;
-    }
-
-    ANativeWindow_Buffer getBufferQueueLayerBuffer(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));
-
-        return buffer;
-    }
-
-    void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
-        ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost());
-
-        // wait for the newly posted buffer to be latched
-        waitForLayerBuffers();
-    }
-
-    virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color,
-                                           int32_t bufferWidth, int32_t bufferHeight) {
-        ANativeWindow_Buffer buffer;
-        ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer));
-        fillANativeWindowBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color);
-        postBufferQueueLayerBuffer(layer);
-    }
-
-    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) {
-        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;
-        }
-    }
-
-    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;
-    }
-
-    void asTransaction(const std::function<void(Transaction&)>& exec) {
-        Transaction t;
-        exec(t);
-        t.apply(true);
-    }
-
-    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->getInternalDisplayToken();
-        ASSERT_FALSE(mDisplay == nullptr) << "failed to get display";
-
-        // get display width/height
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, 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
-        // vsyncs.
-        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() {
-        // 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:
-
-                const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken();
-                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;
-};
-
-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);
-    void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha,
-                                  Color finalColor);
-
-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(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    {
-        SCOPED_TRACE("default position");
-        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");
-        const Rect rect(5, 10, 37, 42);
-        auto shot = getScreenCapture();
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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
-    // loss (b/69315223)
-    const float epsilon = 1.0f / 16.0f;
-    Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
-    {
-        SCOPED_TRACE("rounding down");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply();
-    {
-        SCOPED_TRACE("rounding up");
-        getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setPosition(layer, -32, -32).apply();
-    {
-        SCOPED_TRACE("negative coordinates");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-
-    Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply();
-    {
-        SCOPED_TRACE("positive coordinates");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // partially out of bounds
-    Transaction().setPosition(layer, -30, -30).apply();
-    {
-        SCOPED_TRACE("negative coordinates");
-        getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED);
-    }
-
-    Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply();
-    {
-        SCOPED_TRACE("positive coordinates");
-        getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth,
-                                             mDisplayHeight),
-                                        Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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 = getScreenCapture();
-        const Rect rect(5, 10, 37, 42);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("resize applied");
-        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setPosition(layer, 15, 20).apply();
-    {
-        SCOPED_TRACE("pending new position modified");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setSize(layer, 64, 64).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    // finally resize and latch the buffer
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("new position applied");
-        getScreenCapture()->expectColor(Rect(15, 20, 79, 84), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setPosition is not immediate even with SCALE_TO_WINDOW override
-    Transaction()
-            .setPosition(layer, 5, 10)
-            .setSize(layer, 64, 64)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .setGeometryAppliesWithResize(layer)
-            .apply();
-    {
-        SCOPED_TRACE("new position pending");
-        getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("new position applied");
-        getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setSize(layer, 64, 64).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        auto shot = getScreenCapture();
-        const Rect rect(0, 0, 32, 32);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("resize applied");
-        auto shot = getScreenCapture();
-        const Rect rect(0, 0, 64, 64);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) {
-    // cannot test robustness against invalid sizes (zero or really huge)
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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();
-    getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED);
-}
-
-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, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
-    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
-    {
-        SCOPED_TRACE("layerR");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setLayer(layerG, mLayerZBase + 2).apply();
-    {
-        SCOPED_TRACE("layerG");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
-    }
-}
-
-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, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    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");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setLayer(layerR, -3).apply();
-    {
-        SCOPED_TRACE("layerG");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN);
-    }
-}
-
-void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) {
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    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));
-
-    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 = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
-        shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN);
-    }
-
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply();
-    {
-        SCOPED_TRACE("layerG below");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN);
-    }
-}
-
-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, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    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, 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();
-
-    std::unique_ptr<ScreenCapture> screenshot;
-    // only layerB is in this range
-    sp<IBinder> parentHandle = parent->getHandle();
-    ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32));
-    screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE);
-}
-
-TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) {
-    sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
-                                              ISurfaceComposerClient::eFXSurfaceColor);
-
-    sp<SurfaceControl> childLayer;
-    ASSERT_NO_FATAL_FAILURE(
-            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
-                                                           0 /* buffer height */,
-                                                           ISurfaceComposerClient::eFXSurfaceColor,
-                                                           parent.get()));
-    Transaction()
-            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
-            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
-            .show(childLayer)
-            .show(parent)
-            .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight))
-            .setCrop_legacy(childLayer, Rect(0, 0, 20, 30))
-            .apply();
-
-    Transaction()
-            .setRelativeLayer(childLayer, parent->getHandle(), -1)
-            .setLayer(childLayer, 1)
-            .apply();
-
-    {
-        SCOPED_TRACE("setLayer above");
-        // Set layer should get applied and place the child above.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
-    }
-
-    Transaction()
-            .setLayer(childLayer, 1)
-            .setRelativeLayer(childLayer, parent->getHandle(), -1)
-            .apply();
-
-    {
-        SCOPED_TRACE("setRelative below");
-        // Set relative layer should get applied and place the child below.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
-    }
-}
-
-TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) {
-    sp<SurfaceControl> parent =
-            LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */,
-                                              ISurfaceComposerClient::eFXSurfaceColor);
-    sp<SurfaceControl> relativeParent =
-            LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */,
-                    0 /* buffer height */, ISurfaceComposerClient::eFXSurfaceColor);
-
-    sp<SurfaceControl> childLayer;
-    ASSERT_NO_FATAL_FAILURE(
-            childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */,
-                                                           0 /* buffer height */,
-                                                           ISurfaceComposerClient::eFXSurfaceColor,
-                                                           parent.get()));
-    Transaction()
-            .setColor(childLayer, half3{1.0f, 0.0f, 0.0f})
-            .setColor(parent, half3{0.0f, 0.0f, 0.0f})
-            .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f})
-            .show(childLayer)
-            .show(parent)
-            .show(relativeParent)
-            .setLayer(parent, mLayerZBase - 1)
-            .setLayer(relativeParent, mLayerZBase)
-            .apply();
-
-    Transaction()
-            .setRelativeLayer(childLayer, relativeParent->getHandle(), 1)
-            .apply();
-
-    {
-        SCOPED_TRACE("setLayer above");
-        // Set layer should get applied and place the child above.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED);
-    }
-
-    Transaction()
-        .hide(relativeParent)
-        .apply();
-
-    {
-        SCOPED_TRACE("hide relative parent");
-        // The relative should no longer be visible.
-        std::unique_ptr<ScreenCapture> screenshot;
-        ScreenCapture::captureScreen(&screenshot);
-        screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK);
-    }
-}
-
-void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) {
-    sp<SurfaceControl> layerR;
-    sp<SurfaceControl> layerG;
-    sp<SurfaceControl> layerB;
-    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
-    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 = 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);
-    }
-
-    // layerR = 4, layerG = layerR + 3, layerB = 2
-    Transaction().setLayer(layerR, mLayerZBase + 4).apply();
-    {
-        SCOPED_TRACE("layerB < (layerR < layerG)");
-        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);
-    }
-
-    // layerR = 4, layerG = layerR - 3, layerB = 2
-    Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply();
-    {
-        SCOPED_TRACE("layerB < (layerG < layerR)");
-        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);
-    }
-
-    // restore to absolute z
-    // layerR = 4, layerG = 0, layerB = 2
-    Transaction().setLayer(layerG, mLayerZBase).apply();
-    {
-        SCOPED_TRACE("layerG < layerB < layerR");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::RED);
-        shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE);
-    }
-
-    // layerR should not affect layerG anymore
-    // layerR = 1, layerG = 0, layerB = 2
-    Transaction().setLayer(layerR, mLayerZBase + 1).apply();
-    {
-        SCOPED_TRACE("layerG < layerR < layerB");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 16, 16), Color::RED);
-        shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE);
-    }
-}
-
-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, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
-    Transaction()
-            .setPosition(layerG, 16, 16)
-            .setRelativeLayer(layerG, layerR->getHandle(), 1)
-            .apply();
-
-    layerG.clear();
-    // layerG should have been removed
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-
-    Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply();
-    {
-        SCOPED_TRACE("layer shown");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-}
-
-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, 32, 32));
-    ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32));
-
-    Transaction()
-            .setLayer(layerR, mLayerZBase + 1)
-            .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque)
-            .apply();
-    {
-        SCOPED_TRACE("layerR opaque");
-        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);
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255});
-    }
-}
-
-TEST_P(LayerTypeTransactionTest, SetFlagsSecure) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
-    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
-    sp<GraphicBuffer> outBuffer;
-    Transaction()
-            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
-            .apply(true);
-    ASSERT_EQ(PERMISSION_DENIED,
-              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, false));
-}
-
-/** RAII Wrapper around get/seteuid */
-class UIDFaker {
-    uid_t oldId;
-public:
-    UIDFaker(uid_t uid) {
-        oldId = geteuid();
-        seteuid(uid);
-    }
-    ~UIDFaker() {
-        seteuid(oldId);
-    }
-};
-
-TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    sp<ISurfaceComposer> composer = ComposerService::getComposerService();
-    sp<GraphicBuffer> outBuffer;
-    Transaction()
-            .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
-            .apply(true);
-    ASSERT_EQ(PERMISSION_DENIED,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
-    UIDFaker f(AID_SYSTEM);
-
-    // By default the system can capture screenshots with secure layers but they
-    // will be blacked out
-    ASSERT_EQ(NO_ERROR,
-              composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false));
-
-    {
-        SCOPED_TRACE("as system");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able
-    // to receive them...we are expected to take care with the results.
-    bool outCapturedSecureLayers;
-    ASSERT_EQ(NO_ERROR,
-              composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers,
-                                      ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0,
-                                      0, false, ISurfaceComposer::eRotateNone, true));
-    ASSERT_EQ(true, outCapturedSecureLayers);
-    ScreenCapture sc(outBuffer);
-    sc.expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-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 = 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(postBufferQueueLayerBuffer(layer));
-    {
-        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 pending");
-        auto shot = getScreenCapture();
-        shot->expectColor(top, Color::BLACK);
-        shot->expectColor(bottom, Color::RED);
-    }
-
-    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 = getScreenCapture();
-        shot->expectColor(top, Color::RED);
-        shot->expectColor(bottom, Color::BLACK);
-    }
-}
-
-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));
-    ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32));
-
-    // check that transparent region hint is bound by the layer size
-    Transaction()
-            .setTransparentRegionHint(layerTransparent, Region(mDisplayRect))
-            .setPosition(layerR, 16, 16)
-            .setLayer(layerR, mLayerZBase + 1)
-            .apply();
-    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_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);
-}
-
-void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) {
-    sp<SurfaceControl> layer1;
-    sp<SurfaceControl> layer2;
-    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));
-
-    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 = 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});
-        shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255});
-
-        r /= 4; // r * (1.0f - 0.75f)
-        shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255});
-    }
-}
-
-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, 32, 32));
-
-    Transaction().setAlpha(layer, 2.0f).apply();
-    {
-        SCOPED_TRACE("clamped to 1.0f");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color);
-    }
-
-    Transaction().setAlpha(layer, -1.0f).apply();
-    {
-        SCOPED_TRACE("clamped to 0.0f");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-}
-
-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(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))
-            .setLayer(colorLayer, mLayerZBase + 1)
-            .apply();
-
-    {
-        SCOPED_TRACE("default color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f);
-    const Color expected = {15, 51, 85, 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).apply();
-    {
-        SCOPED_TRACE("new color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance);
-    }
-}
-
-// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill
-// BLUE: prior background color
-// GREEN: final background color
-// BLACK: no color or fill
-void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor,
-                                                              bool bufferFill, float alpha,
-                                                              Color finalColor) {
-    sp<SurfaceControl> layer;
-    int32_t width = 500;
-    int32_t height = 500;
-
-    Color fillColor = Color::RED;
-    Color priorBgColor = Color::BLUE;
-    Color expectedColor = Color::BLACK;
-    switch (layerType) {
-        case ISurfaceComposerClient::eFXSurfaceColor:
-            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType));
-            Transaction()
-                    .setCrop_legacy(layer, Rect(0, 0, width, height))
-                    .setColor(layer, half3(1.0f, 0, 0))
-                    .apply();
-            expectedColor = fillColor;
-            break;
-        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
-            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
-            if (bufferFill) {
-                ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height));
-                expectedColor = fillColor;
-            }
-            Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply();
-            break;
-        case ISurfaceComposerClient::eFXSurfaceBufferState:
-            ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType));
-            if (bufferFill) {
-                ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height));
-                expectedColor = fillColor;
-            }
-            Transaction().setFrame(layer, Rect(0, 0, width, height)).apply();
-            break;
-        default:
-            GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper";
-            return;
-    }
-
-    if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) {
-        Transaction()
-                .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN)
-                .apply();
-        if (!bufferFill) {
-            expectedColor = priorBgColor;
-        }
-    }
-
-    {
-        SCOPED_TRACE("default before setting background color layer");
-        screenshot()->expectColor(Rect(0, 0, width, height), expectedColor);
-    }
-    Transaction()
-            .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN)
-            .apply();
-
-    {
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, width, height), finalColor);
-        shot->expectBorder(Rect(0, 0, width, height), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = true;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) {
-    bool priorColor = true;
-    bool bufferFill = true;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = true;
-    float alpha = 1.0f;
-    Color finalColor = Color::RED;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 1.0f;
-    Color finalColor = Color::GREEN;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) {
-    bool priorColor = false;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-TEST_P(LayerRenderTypeTransactionTest,
-       SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) {
-    bool priorColor = true;
-    bool bufferFill = false;
-    float alpha = 0;
-    Color finalColor = Color::BLACK;
-    ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState,
-                                                     priorColor, bufferFill, alpha, finalColor));
-}
-
-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(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;
-    const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f);
-    // 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)
-            .setAlpha(colorLayer, alpha)
-            .setLayer(colorLayer, mLayerZBase + 1)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                                    tolerance);
-}
-
-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(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);
-    // this is handwavy, but the precision loss scaled by 255 (8-bit per
-    // channel) should be less than one
-    const uint8_t tolerance = 1;
-    Transaction()
-            .reparent(colorLayer, parentLayer->getHandle())
-            .setColor(colorLayer, color)
-            .setAlpha(parentLayer, alpha)
-            .setLayer(parentLayer, mLayerZBase + 1)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255},
-                                    tolerance);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) {
-    sp<SurfaceControl> bufferLayer;
-    ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32));
-
-    // color is ignored
-    Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-}
-
-TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32));
-
-    Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply();
-    {
-        SCOPED_TRACE("non-existing layer stack");
-        getScreenCapture()->expectColor(mDisplayRect, Color::BLACK);
-    }
-
-    Transaction().setLayerStack(layer, mDisplayLayerStack).apply();
-    {
-        SCOPED_TRACE("original layer stack");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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");
-        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");
-        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");
-        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");
-        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");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE, true /* filtered */);
-    }
-}
-
-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(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 = getScreenCapture();
-    // check a 8x8 region inside each color
-    auto get8x8Rect = [](int32_t centerX, int32_t centerY) {
-        const int32_t halfL = 4;
-        return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL);
-    };
-    const int32_t unit = int32_t(trans / 2);
-    shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED);
-    shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN);
-    shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE);
-    shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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 = getScreenCapture();
-        const Rect rect(0, 0, 32, 32);
-        shot->expectColor(rect, Color::RED);
-        shot->expectBorder(rect, Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64));
-    {
-        SCOPED_TRACE("resize applied");
-        const Rect rect(0, 0, 128, 128);
-        getScreenCapture()->expectColor(rect, Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition
-    Transaction()
-            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setSize(layer, 64, 64)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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
-    // (b/69315456)
-    Transaction()
-            .setSize(layer, 64, 16)
-            .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)
-            .apply();
-    {
-        SCOPED_TRACE("SCALE_TO_WINDOW");
-        getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN,
-                                           Color::BLUE, Color::WHITE, true /* filtered */);
-    }
-}
-
-TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-
-    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_legacy(layer, crop).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(crop, Color::RED);
-    shot->expectBorder(crop, Color::BLACK);
-}
-
-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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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();
-        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    }
-
-    {
-        SCOPED_TRACE("negative rect");
-        Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    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_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState));
-    sp<GraphicBuffer> buffer =
-            new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1,
-                              BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                      BufferUsage::COMPOSER_OVERLAY,
-                              "test");
-    fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE);
-    fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED);
-
-    Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply();
-
-    Transaction().setBuffer(layer, buffer).apply();
-
-    // Partially out of bounds in the negative (upper left) direction
-    Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply();
-    {
-        SCOPED_TRACE("out of bounds, negative (upper left) direction");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE);
-        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
-    }
-
-    // Partially out of bounds in the positive (lower right) direction
-    Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply();
-    {
-        SCOPED_TRACE("out of bounds, positive (lower right) direction");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(0, 0, 64, 64), Color::RED);
-        shot->expectBorder(Rect(0, 0, 64, 64), 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, 64, 16), Color::BLUE);
-        shot->expectColor(Rect(0, 16, 64, 64), Color::RED);
-        shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    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_legacy(layer, crop).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(crop + position, Color::RED);
-    shot->expectBorder(crop + position, Color::BLACK);
-}
-
-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(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // crop_legacy is affected by matrix
-    Transaction()
-            .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f)
-            .setCrop_legacy(layer, Rect(8, 8, 24, 24))
-            .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, SetCropWithResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // 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 = getScreenCapture();
-        shot->expectColor(Rect(8, 8, 24, 24), Color::RED);
-        shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK);
-    }
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
-    {
-        SCOPED_TRACE("resize applied");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(8, 8, 16, 16), Color::RED);
-        shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResize_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // 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");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply();
-    {
-        SCOPED_TRACE("pending crop modified");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    Transaction().setSize(layer, 16, 16).apply();
-    {
-        SCOPED_TRACE("resize pending");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED);
-    }
-
-    // finally resize
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
-    {
-        SCOPED_TRACE("new crop applied");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
-        shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
-
-    // setCrop_legacy is not immediate even with SCALE_TO_WINDOW override
-    Transaction()
-            .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 = 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(fillBufferQueueLayerColor(layer, Color::RED, 16, 16));
-    Transaction().setPosition(layer, 0, 0).apply();
-    {
-        SCOPED_TRACE("new crop applied");
-        auto shot = getScreenCapture();
-        shot->expectColor(Rect(4, 4, 12, 12), Color::RED);
-        shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK);
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_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(8, 8, 24, 24);
-
-    Transaction().setFrame(layer, frame).apply();
-    auto shot = getScreenCapture();
-    shot->expectColor(frame, Color::RED);
-    shot->expectBorder(frame, Color::BLACK);
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_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().setFrame(layer, Rect(8, 8, 8, 8)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-
-    {
-        SCOPED_TRACE("negative rect");
-        Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply();
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK);
-    }
-}
-
-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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
-        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::BLUE);
-        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
-        shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, SetBufferCaching_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
-    std::array<sp<GraphicBuffer>, 10> buffers;
-
-    size_t idx = 0;
-    for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
-        Color color = colors[idx % colors.size()];
-        fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
-        idx++;
-    }
-
-    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
-    // cache is working.
-    idx = 0;
-    for (auto& buffer : buffers) {
-        for (int i = 0; i < 2; i++) {
-            Transaction().setBuffer(layer, buffer).apply();
-
-            Color color = colors[idx % colors.size()];
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
-            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-        }
-        idx++;
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
-    std::array<sp<GraphicBuffer>, 70> buffers;
-
-    size_t idx = 0;
-    for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
-        Color color = colors[idx % colors.size()];
-        fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
-        idx++;
-    }
-
-    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
-    // cache is working.
-    idx = 0;
-    for (auto& buffer : buffers) {
-        for (int i = 0; i < 2; i++) {
-            Transaction().setBuffer(layer, buffer).apply();
-
-            Color color = colors[idx % colors.size()];
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
-            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-        }
-        idx++;
-    }
-}
-
-TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState));
-
-    std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN};
-
-    std::array<sp<GraphicBuffer>, 65> buffers;
-
-    size_t idx = 0;
-    for (auto& buffer : buffers) {
-        buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1,
-                                   BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
-                                           BufferUsage::COMPOSER_OVERLAY,
-                                   "test");
-        Color color = colors[idx % colors.size()];
-        fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color);
-        idx++;
-    }
-
-    // Set each buffer twice. The first time adds it to the cache, the second time tests that the
-    // cache is working.
-    idx = 0;
-    for (auto& buffer : buffers) {
-        for (int i = 0; i < 2; i++) {
-            Transaction().setBuffer(layer, buffer).apply();
-
-            Color color = colors[idx % colors.size()];
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color);
-            shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-        }
-        if (idx == 0) {
-            buffers[0].clear();
-        }
-        idx++;
-    }
-}
-
-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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK);
-}
-
-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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, mDisplayWidth, mDisplayHeight), Color::RED);
-    shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), 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, 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);
-    }
-}
-
-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);
-    }
-
-    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()
-            .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32))
-            .setLayer(colorLayer, 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 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(colorLayer, matrix, vec3()).apply();
-    {
-        SCOPED_TRACE("new color");
-        getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance);
-    }
-}
-
-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()));
-
-    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 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, bool setBuffer = true,
-                               bool setBackgroundColor = false) {
-        if (layer) {
-            sp<GraphicBuffer> buffer;
-            sp<Fence> fence;
-            if (setBuffer) {
-                int err = getBuffer(&buffer, &fence);
-                if (err != NO_ERROR) {
-                    return err;
-                }
-
-                transaction.setBuffer(layer, buffer);
-                transaction.setAcquireFence(layer, fence);
-            }
-
-            if (setBackgroundColor) {
-                transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f,
-                                               ui::Dataspace::UNKNOWN);
-            }
-        }
-
-        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, BufferColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, true, true);
-    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, NoBufferNoColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, false, false);
-    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, BufferNoColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, true, false);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
-}
-
-TEST_F(LayerCallbackTest, NoBufferColor) {
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
-
-    Transaction transaction;
-    CallbackHelper callback;
-    int err = fillTransaction(transaction, &callback, layer, false, true);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-
-    transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply();
-
-    ExpectedResult expected;
-    expected.addSurface(ExpectedResult::Transaction::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, MergeBufferNoColor) {
-    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, MergeNoBufferColor) {
-    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, false, true);
-    if (err) {
-        GTEST_SUCCEED() << "test not supported";
-        return;
-    }
-    err = fillTransaction(transaction2, &callback2, layer2, false, true);
-    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::NOT_ACQUIRED);
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true));
-    EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true));
-}
-
-TEST_F(LayerCallbackTest, MergeOneBufferOneColor) {
-    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, false, true);
-    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.addSurface(ExpectedResult::Transaction::PRESENTED, layer1);
-    expected.addSurface(ExpectedResult::Transaction::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, 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() {
-        LayerTransactionTest::SetUp();
-        ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
-        ASSERT_FALSE(display == nullptr);
-
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-        ssize_t displayWidth = info.w;
-        ssize_t displayHeight = info.h;
-
-        // Background surface
-        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 = createLayer(String8("FG Test Surface"), 64, 64, 0);
-
-        ASSERT_TRUE(mFGSurfaceControl != nullptr);
-        ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-        fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-
-        // Synchronization surface
-        mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0);
-        ASSERT_TRUE(mSyncSurfaceControl != nullptr);
-        ASSERT_TRUE(mSyncSurfaceControl->isValid());
-
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-
-        asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
-
-            t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl);
-
-            t.setLayer(mFGSurfaceControl, INT32_MAX - 1)
-                    .setPosition(mFGSurfaceControl, 64, 64)
-                    .show(mFGSurfaceControl);
-
-            t.setLayer(mSyncSurfaceControl, INT32_MAX - 1)
-                    .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2)
-                    .show(mSyncSurfaceControl);
-        });
-    }
-
-    virtual void TearDown() {
-        LayerTransactionTest::TearDown();
-        mBGSurfaceControl = 0;
-        mFGSurfaceControl = 0;
-        mSyncSurfaceControl = 0;
-    }
-
-    void waitForPostedBuffers() {
-        // Since the sync surface is in synchronous mode (i.e. double buffered)
-        // posting three buffers to it should ensure that at least two
-        // SurfaceFlinger::handlePageFlip calls have been made, which should
-        // guaranteed that a buffer posted to another Surface has been retired.
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-        fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-    }
-
-
-    sp<SurfaceControl> mBGSurfaceControl;
-    sp<SurfaceControl> mFGSurfaceControl;
-
-    // This surface is used to ensure that the buffers posted to
-    // mFGSurfaceControl have been picked up by SurfaceFlinger.
-    sp<SurfaceControl> mSyncSurfaceControl;
-};
-
-TEST_F(LayerUpdateTest, RelativesAreNotDetached) {
-
-    std::unique_ptr<ScreenCapture> sc;
-
-    sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0);
-    fillSurfaceRGBA8(relative, 10, 10, 10);
-    waitForPostedBuffers();
-
-    Transaction{}
-            .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1)
-            .setPosition(relative, 64, 64)
-            .apply();
-
-    {
-        // The relative should be on top of the FG control.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(64, 64, 10, 10, 10);
-    }
-    Transaction{}.detachChildren(mFGSurfaceControl).apply();
-
-    {
-        // Nothing should change at this point.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(64, 64, 10, 10, 10);
-    }
-
-    Transaction{}.hide(relative).apply();
-
-    {
-        // Ensure that the relative was actually hidden, rather than
-        // being left in the detached but visible state.
-        ScreenCapture::captureScreen(&sc);
-        sc->expectFGColor(64, 64);
-    }
-}
-
-class GeometryLatchingTest : public LayerUpdateTest {
-protected:
-    void EXPECT_INITIAL_STATE(const char* trace) {
-        SCOPED_TRACE(trace);
-        ScreenCapture::captureScreen(&sc);
-        // We find the leading edge of the FG surface.
-        sc->expectFGColor(127, 127);
-        sc->expectBGColor(128, 128);
-    }
-
-    void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false); }
-
-    void unlockFGBuffer() {
-        sp<Surface> s = mFGSurfaceControl->getSurface();
-        ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-        waitForPostedBuffers();
-    }
-
-    void completeFGResize() {
-        fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-        waitForPostedBuffers();
-    }
-    void restoreInitialState() {
-        asTransaction([&](Transaction& t) {
-            t.setSize(mFGSurfaceControl, 64, 64);
-            t.setPosition(mFGSurfaceControl, 64, 64);
-            t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
-        });
-
-        EXPECT_INITIAL_STATE("After restoring initial state");
-    }
-    std::unique_ptr<ScreenCapture> sc;
-};
-
-class CropLatchingTest : public GeometryLatchingTest {
-protected:
-    void EXPECT_CROPPED_STATE(const char* trace) {
-        SCOPED_TRACE(trace);
-        ScreenCapture::captureScreen(&sc);
-        // The edge should be moved back one pixel by our crop.
-        sc->expectFGColor(126, 126);
-        sc->expectBGColor(127, 127);
-        sc->expectBGColor(128, 128);
-    }
-
-    void EXPECT_RESIZE_STATE(const char* trace) {
-        SCOPED_TRACE(trace);
-        ScreenCapture::captureScreen(&sc);
-        // The FG is now resized too 128,128 at 64,64
-        sc->expectFGColor(64, 64);
-        sc->expectFGColor(191, 191);
-        sc->expectBGColor(192, 192);
-    }
-};
-
-TEST_F(LayerUpdateTest, DeferredTransactionTest) {
-    std::unique_ptr<ScreenCapture> sc;
-    {
-        SCOPED_TRACE("before anything");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->expectFGColor(96, 96);
-        sc->expectBGColor(160, 160);
-    }
-
-    // set up two deferred transactions on different frames
-    asTransaction([&](Transaction& t) {
-        t.setAlpha(mFGSurfaceControl, 0.75);
-        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
-                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber());
-    });
-
-    asTransaction([&](Transaction& t) {
-        t.setPosition(mFGSurfaceControl, 128, 128);
-        t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(),
-                                       mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
-    });
-
-    {
-        SCOPED_TRACE("before any trigger");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->expectFGColor(96, 96);
-        sc->expectBGColor(160, 160);
-    }
-
-    // should trigger the first deferred transaction, but not the second one
-    fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-    {
-        SCOPED_TRACE("after first trigger");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->checkPixel(96, 96, 162, 63, 96);
-        sc->expectBGColor(160, 160);
-    }
-
-    // should show up immediately since it's not deferred
-    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); });
-
-    // trigger the second deferred transaction
-    fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
-    {
-        SCOPED_TRACE("after second trigger");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(32, 32);
-        sc->expectBGColor(96, 96);
-        sc->expectFGColor(160, 160);
-    }
-}
-
-TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) {
-    std::unique_ptr<ScreenCapture> sc;
-
-    sp<SurfaceControl> childNoBuffer =
-            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{}
-            .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{}
-            .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20))
-            .apply(true);
-    {
-        ScreenCapture::captureScreen(&sc);
-        sc->expectChildColor(73, 73);
-        sc->expectChildColor(74, 74);
-    }
-}
-
-TEST_F(LayerUpdateTest, MergingTransactions) {
-    std::unique_ptr<ScreenCapture> sc;
-    {
-        SCOPED_TRACE("before move");
-        ScreenCapture::captureScreen(&sc);
-        sc->expectBGColor(0, 12);
-        sc->expectFGColor(75, 75);
-        sc->expectBGColor(145, 145);
-    }
-
-    Transaction t1, t2;
-    t1.setPosition(mFGSurfaceControl, 128, 128);
-    t2.setPosition(mFGSurfaceControl, 0, 0);
-    // We expect that the position update from t2 now
-    // overwrites the position update from t1.
-    t1.merge(std::move(t2));
-    t1.apply();
-
-    {
-        ScreenCapture::captureScreen(&sc);
-        sc->expectFGColor(1, 1);
-    }
-}
-
-class ChildLayerTest : public LayerUpdateTest {
-protected:
-    void SetUp() override {
-        LayerUpdateTest::SetUp();
-        mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0,
-                               mFGSurfaceControl.get());
-        fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-        {
-            SCOPED_TRACE("before anything");
-            mCapture = screenshot();
-            mCapture->expectChildColor(64, 64);
-        }
-    }
-    void TearDown() override {
-        LayerUpdateTest::TearDown();
-        mChild = 0;
-    }
-
-    sp<SurfaceControl> mChild;
-    std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ChildLayerTest, ChildLayerPositioning) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        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);
-    }
-
-    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
-
-    {
-        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
-        mCapture->expectChildColor(10, 10);
-        // And 10 more pixels we should be back to the foreground surface
-        mCapture->expectFGColor(20, 20);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerCropping) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(4, 4);
-        mCapture->expectBGColor(5, 5);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerConstraints) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setPosition(mChild, 63, 63);
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(0, 0);
-        // Last pixel in foreground should now be the child.
-        mCapture->expectChildColor(63, 63);
-        // But the child should be constrained and the next pixel
-        // must be the background
-        mCapture->expectBGColor(64, 64);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerScaling) {
-    asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); });
-
-    // Find the boundary between the parent and child
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(9, 9);
-        mCapture->expectFGColor(10, 10);
-    }
-
-    asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); });
-
-    // The boundary should be twice as far from the origin now.
-    // The pixels from the last test should all be child now
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(9, 9);
-        mCapture->expectChildColor(10, 10);
-        mCapture->expectChildColor(19, 19);
-        mCapture->expectFGColor(20, 20);
-    }
-}
-
-// A child with a scale transform should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) {
-    asTransaction([&](Transaction& t) {
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setPosition(mChild, 0, 0);
-    });
-
-    // Find the boundary between the parent and child.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(9, 9);
-        mCapture->expectFGColor(10, 10);
-    }
-
-    asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); });
-
-    // The child should fill its parent bounds and be cropped by it.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(63, 63);
-        mCapture->expectBGColor(64, 64);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerAlpha) {
-    fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254);
-    fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0);
-    fillSurfaceRGBA8(mChild, 0, 254, 0);
-    waitForPostedBuffers();
-
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        // Unblended child color
-        mCapture->checkPixel(0, 0, 0, 254, 0);
-    }
-
-    asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); });
-
-    {
-        mCapture = screenshot();
-        // Child and BG blended.
-        mCapture->checkPixel(0, 0, 127, 127, 0);
-    }
-
-    asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); });
-
-    {
-        mCapture = screenshot();
-        // Child and BG blended.
-        mCapture->checkPixel(0, 0, 95, 64, 95);
-    }
-}
-
-TEST_F(ChildLayerTest, ReparentChildren) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        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);
-    }
-
-    asTransaction([&](Transaction& t) {
-        t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        // In reparenting we should have exposed the entire foreground surface.
-        mCapture->expectFGColor(74, 74);
-        // And the child layer should now begin at 10, 10 (since the BG
-        // layer is at (0, 0)).
-        mCapture->expectBGColor(9, 9);
-        mCapture->expectChildColor(10, 10);
-    }
-}
-
-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);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        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);
-    }
-
-
-    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
-    asTransaction([&](Transaction& t) { t.hide(mChild); });
-
-    // Since the child has the same client as the parent, it will not get
-    // detached and will be hidden.
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectFGColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
-    sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient;
-    sp<SurfaceControl> mChildNewClient =
-            createSurface(mNewComposerClient, "New Child Test Surface", 10, 10,
-                          PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-
-    ASSERT_TRUE(mChildNewClient->isValid());
-
-    fillSurfaceRGBA8(mChildNewClient, 200, 200, 200);
-
-    asTransaction([&](Transaction& t) {
-        t.hide(mChild);
-        t.show(mChildNewClient);
-        t.setPosition(mChildNewClient, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        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);
-    }
-
-    asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); });
-
-    asTransaction([&](Transaction& t) { t.hide(mChildNewClient); });
-
-    // Nothing should have changed.
-    {
-        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, DetachChildrenWithDeferredTransaction) {
-    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();
-        Rect rect = Rect(74, 74, 84, 84);
-        mCapture->expectBorder(rect, Color{195, 63, 63, 255});
-        mCapture->expectColor(rect, Color{200, 200, 200, 255});
-    }
-
-    Transaction()
-            .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(),
-                                          mFGSurfaceControl->getSurface()->getNextFrameNumber())
-            .apply();
-    Transaction().detachChildren(mFGSurfaceControl).apply();
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32));
-
-    // BufferLayer can still dequeue buffers even though there's a detached layer with a
-    // deferred transaction.
-    {
-        SCOPED_TRACE("new buffer");
-        mCapture = screenshot();
-        Rect rect = Rect(74, 74, 84, 84);
-        mCapture->expectBorder(rect, Color::RED);
-        mCapture->expectColor(rect, Color{200, 200, 200, 255});
-    }
-}
-
-TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        // We've positioned the child in the top left.
-        mCapture->expectChildColor(0, 0);
-        // But it's only 10x15.
-        mCapture->expectFGColor(10, 15);
-    }
-
-    asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // We cause scaling by 2.
-        t.setSize(mFGSurfaceControl, 128, 128);
-    });
-
-    {
-        mCapture = screenshot();
-        // We've positioned the child in the top left.
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(10, 10);
-        mCapture->expectChildColor(19, 29);
-        // And now it should be scaled all the way to 20x30
-        mCapture->expectFGColor(20, 30);
-    }
-}
-
-// Regression test for b/37673612
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        // We've positioned the child in the top left.
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(9, 14);
-        // But it's only 10x15.
-        mCapture->expectFGColor(10, 15);
-    }
-    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
-    // the WM specified state size.
-    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 64, 128);
-    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-    waitForPostedBuffers();
-
-    {
-        // The child should still be in the same place and not have any strange scaling as in
-        // b/37673612.
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectFGColor(10, 10);
-    }
-}
-
-// A child with a buffer transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setSize(mChild, 100, 100);
-    });
-    fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-    {
-        mCapture = screenshot();
-
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(63, 63);
-        mCapture->expectBGColor(64, 64);
-    }
-
-    asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); });
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    // Apply a 90 transform on the buffer.
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 64, 128);
-    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-    waitForPostedBuffers();
-
-    // The child should be cropped by the new parent bounds.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(99, 63);
-        mCapture->expectFGColor(100, 63);
-        mCapture->expectBGColor(128, 64);
-    }
-}
-
-// A child with a scale transform from its parents should be cropped by its parent bounds.
-TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-        t.setSize(mChild, 200, 200);
-    });
-    fillSurfaceRGBA8(mChild, 200, 200, 200);
-
-    {
-        mCapture = screenshot();
-
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(63, 63);
-        mCapture->expectBGColor(64, 64);
-    }
-
-    asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // Set a scaling by 2.
-        t.setSize(mFGSurfaceControl, 128, 128);
-    });
-
-    // Child should inherit its parents scale but should be cropped by its parent bounds.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(127, 127);
-        mCapture->expectBGColor(128, 128);
-    }
-}
-
-// Regression test for b/127368943
-// Child should ignore the buffer transform but apply parent scale transform.
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 0, 0);
-        t.setPosition(mFGSurfaceControl, 0, 0);
-    });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(9, 14);
-        mCapture->expectFGColor(10, 15);
-    }
-
-    // Change the size of the foreground to 128 * 64 so we can test rotation as well.
-    asTransaction([&](Transaction& t) {
-        t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        t.setSize(mFGSurfaceControl, 128, 64);
-    });
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we
-    // have an effective scale of 2.0 applied to the buffer along with a rotation transform.
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 32, 64);
-    fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63);
-    waitForPostedBuffers();
-
-    // The child should ignore the buffer transform but apply the 2.0 scale from parent.
-    {
-        mCapture = screenshot();
-        mCapture->expectChildColor(0, 0);
-        mCapture->expectChildColor(19, 29);
-        mCapture->expectFGColor(20, 30);
-    }
-}
-
-TEST_F(ChildLayerTest, Bug36858924) {
-    // Destroy the child layer
-    mChild.clear();
-
-    // Now recreate it as hidden
-    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_legacy(mChild, mFGSurfaceControl->getHandle(),
-                                       mFGSurfaceControl->getSurface()->getNextFrameNumber());
-        t.show(mChild);
-    });
-
-    // Render the foreground surface a few times
-    //
-    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
-    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
-    // never acquire/release the first buffer
-    ALOGI("Filling 1");
-    fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
-    ALOGI("Filling 2");
-    fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255);
-    ALOGI("Filling 3");
-    fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0);
-    ALOGI("Filling 4");
-    fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
-}
-
-TEST_F(ChildLayerTest, Reparent) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        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);
-    }
-
-    asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); });
-
-    {
-        mCapture = screenshot();
-        mCapture->expectFGColor(64, 64);
-        // In reparenting we should have exposed the entire foreground surface.
-        mCapture->expectFGColor(74, 74);
-        // And the child layer should now begin at 10, 10 (since the BG
-        // layer is at (0, 0)).
-        mCapture->expectBGColor(9, 9);
-        mCapture->expectChildColor(10, 10);
-    }
-}
-
-TEST_F(ChildLayerTest, ReparentToNoParent) {
-    asTransaction([&](Transaction& t) {
-        t.show(mChild);
-        t.setPosition(mChild, 10, 10);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        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);
-    }
-    asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); });
-    {
-        mCapture = screenshot();
-        // The surface should now be offscreen.
-        mCapture->expectFGColor(64, 64);
-        mCapture->expectFGColor(74, 74);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, ReparentFromNoParent) {
-    sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0);
-    ASSERT_TRUE(newSurface != nullptr);
-    ASSERT_TRUE(newSurface->isValid());
-
-    fillSurfaceRGBA8(newSurface, 63, 195, 63);
-    asTransaction([&](Transaction& t) {
-        t.hide(mChild);
-        t.show(newSurface);
-        t.setPosition(newSurface, 10, 10);
-        t.setLayer(newSurface, INT32_MAX - 2);
-        t.setPosition(mFGSurfaceControl, 64, 64);
-    });
-
-    {
-        mCapture = screenshot();
-        // Top left of foreground must now be visible
-        mCapture->expectFGColor(64, 64);
-        // At 10, 10 we should see the new surface
-        mCapture->checkPixel(10, 10, 63, 195, 63);
-    }
-
-    asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); });
-
-    {
-        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);
-        mCapture->checkPixel(74, 74, 63, 195, 63);
-        mCapture->expectFGColor(84, 84);
-    }
-}
-
-TEST_F(ChildLayerTest, NestedChildren) {
-    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, mChild.get());
-    fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
-    {
-        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);
-    }
-}
-
-TEST_F(ChildLayerTest, ChildLayerRelativeLayer) {
-    sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0);
-    fillSurfaceRGBA8(relative, 255, 255, 255);
-
-    Transaction t;
-    t.setLayer(relative, INT32_MAX)
-            .setRelativeLayer(mChild, relative->getHandle(), 1)
-            .setPosition(mFGSurfaceControl, 0, 0)
-            .apply(true);
-
-    // We expect that the child should have been elevated above our
-    // INT_MAX layer even though it's not a child of it.
-    {
-        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:
-    std::unique_ptr<ScreenCapture> mCapture;
-};
-
-TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
-    auto bgHandle = mBGSurfaceControl->getHandle();
-    ScreenCapture::captureLayers(&mCapture, bgHandle);
-    mCapture->expectBGColor(0, 0);
-    // Doesn't capture FG layer which is at 64, 64
-    mCapture->expectBGColor(64, 64);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    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);
-
-    // Captures mFGSurfaceControl layer and its child.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    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);
-
-    // Captures mFGSurfaceControl's child
-    ScreenCapture::captureChildLayers(&mCapture, fgHandle);
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureLayerExclude) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child2, 200, 0, 200);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .show(child2)
-            .setLayer(child, 1)
-            .setLayer(child2, 2)
-            .apply(true);
-
-    // Child2 would be visible but its excluded, so we should see child1 color instead.
-    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-// Like the last test but verifies that children are also exclude.
-TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-    sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child2, 200, 0, 200);
-    sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10,
-                                              PIXEL_FORMAT_RGBA_8888, 0, child2.get());
-    fillSurfaceRGBA8(child2, 200, 0, 200);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .show(child2)
-            .show(child3)
-            .setLayer(child, 1)
-            .setLayer(child2, 2)
-            .apply(true);
-
-    // Child2 would be visible but its excluded, so we should see child1 color instead.
-    ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()});
-    mCapture->checkPixel(10, 10, 0, 0, 0);
-    mCapture->checkPixel(0, 0, 200, 200, 200);
-}
-
-TEST_F(ScreenCaptureTest, CaptureTransparent) {
-    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);
-
-    auto childHandle = child->getHandle();
-
-    // Captures child
-    ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20});
-    mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255});
-    // Area outside of child's bounds is transparent.
-    mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0});
-}
-
-TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    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);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            // Set relative layer above fg layer so should be shown above when computing all layers.
-            .setRelativeLayer(relative, fgHandle, 1)
-            .show(relative)
-            .apply(true);
-
-    // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureRelativeInTree) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    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);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            // Set relative layer below fg layer but relative to child layer so it should be shown
-            // above child layer.
-            .setLayer(relative, -1)
-            .setRelativeLayer(relative, child->getHandle(), 1)
-            .show(relative)
-            .apply(true);
-
-    // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its
-    // relative value should be taken into account, placing it above child layer.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    // Relative layer is showing on top of child layer
-    mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
-}
-
-// In the following tests we verify successful skipping of a parent layer,
-// so we use the same verification logic and only change how we mutate
-// the parent layer to verify that various properties are ignored.
-class ScreenCaptureChildOnlyTest : public LayerUpdateTest {
-public:
-    void SetUp() override {
-        LayerUpdateTest::SetUp();
-
-        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);
-    }
-
-    void verify(std::function<void()> verifyStartingState) {
-        // Verify starting state before a screenshot is taken.
-        verifyStartingState();
-
-        // Verify child layer does not inherit any of the properties of its
-        // parent when its screenshot is captured.
-        auto fgHandle = mFGSurfaceControl->getHandle();
-        ScreenCapture::captureChildLayers(&mCapture, fgHandle);
-        mCapture->checkPixel(10, 10, 0, 0, 0);
-        mCapture->expectChildColor(0, 0);
-
-        // Verify all assumptions are still true after the screenshot is taken.
-        verifyStartingState();
-    }
-
-    std::unique_ptr<ScreenCapture> mCapture;
-    sp<SurfaceControl> mChild;
-};
-
-// Regression test b/76099859
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) {
-
-    SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true);
-
-    // Even though the parent is hidden we should still capture the child.
-
-    // Before and after reparenting, verify child is properly hidden
-    // when rendering full-screen.
-    verify([&] { screenshot()->expectBGColor(64, 64); });
-}
-
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) {
-    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.
-
-    // Before and after reparenting, verify child is cropped by parent.
-    verify([&] { screenshot()->expectBGColor(65, 65); });
-}
-
-// Regression test b/124372894
-TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) {
-    SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true);
-
-    // We should not inherit the parent scaling.
-
-    // Before and after reparenting, verify child is properly scaled.
-    verify([&] { screenshot()->expectChildColor(80, 80); });
-}
-
-
-TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
-    auto fgHandle = mFGSurfaceControl->getHandle();
-
-    sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
-                                             PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
-    fillSurfaceRGBA8(child, 200, 200, 200);
-
-    sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
-
-    fillSurfaceRGBA8(grandchild, 50, 50, 50);
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .setPosition(grandchild, 5, 5)
-            .show(grandchild)
-            .apply(true);
-
-    // Captures mFGSurfaceControl, its child, and the grandchild.
-    ScreenCapture::captureLayers(&mCapture, fgHandle);
-    mCapture->expectFGColor(10, 10);
-    mCapture->expectChildColor(0, 0);
-    mCapture->checkPixel(5, 5, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureChildOnly) {
-    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();
-
-    SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true);
-
-    // Captures only the child layer, and not the parent.
-    ScreenCapture::captureLayers(&mCapture, childHandle);
-    mCapture->expectChildColor(0, 0);
-    mCapture->expectChildColor(9, 9);
-}
-
-TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
-    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 = createSurface(mClient, "Grandchild surface", 5, 5,
-                                                  PIXEL_FORMAT_RGBA_8888, 0, child.get());
-    fillSurfaceRGBA8(grandchild, 50, 50, 50);
-
-    SurfaceComposerClient::Transaction()
-            .show(child)
-            .setPosition(grandchild, 5, 5)
-            .show(grandchild)
-            .apply(true);
-
-    auto grandchildHandle = grandchild->getHandle();
-
-    // Captures only the grandchild.
-    ScreenCapture::captureLayers(&mCapture, grandchildHandle);
-    mCapture->checkPixel(0, 0, 50, 50, 50);
-    mCapture->checkPixel(4, 4, 50, 50, 50);
-}
-
-TEST_F(ScreenCaptureTest, CaptureCrop) {
-    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(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
-    SurfaceComposerClient::Transaction()
-            .setLayer(redLayer, INT32_MAX - 1)
-            .show(redLayer)
-            .show(blueLayer)
-            .apply(true);
-
-    auto redLayerHandle = redLayer->getHandle();
-
-    // Capturing full screen should have both red and blue are visible.
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
-    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.
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureSize) {
-    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(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30));
-
-    SurfaceComposerClient::Transaction()
-            .setLayer(redLayer, INT32_MAX - 1)
-            .show(redLayer)
-            .show(blueLayer)
-            .apply(true);
-
-    auto redLayerHandle = redLayer->getHandle();
-
-    // Capturing full screen should have both red and blue are visible.
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle);
-    mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED);
-
-    ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5);
-    // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area.
-    mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE);
-    // red area below the blue area
-    mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED);
-    // red area to the right of the blue area
-    mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED);
-    mCapture->checkPixel(30, 30, 0, 0, 0);
-}
-
-TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
-    sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0);
-
-    ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-
-    auto redLayerHandle = redLayer->getHandle();
-    redLayer.clear();
-    SurfaceComposerClient::Transaction().apply(true);
-
-    sp<GraphicBuffer> outBuffer;
-
-    // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0));
-}
-
-
-class DereferenceSurfaceControlTest : public LayerTransactionTest {
-protected:
-    void SetUp() override {
-        LayerTransactionTest::SetUp();
-        bgLayer = createLayer("BG layer", 20, 20);
-        fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20);
-        fgLayer = createLayer("FG layer", 20, 20);
-        fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20);
-        Transaction().setLayer(fgLayer, mLayerZBase + 1).apply();
-        {
-            SCOPED_TRACE("before anything");
-            auto shot = screenshot();
-            shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
-        }
-    }
-    void TearDown() override {
-        LayerTransactionTest::TearDown();
-        bgLayer = 0;
-        fgLayer = 0;
-    }
-
-    sp<SurfaceControl> bgLayer;
-    sp<SurfaceControl> fgLayer;
-};
-
-TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) {
-    fgLayer = nullptr;
-    {
-        SCOPED_TRACE("after setting null");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 20, 20), Color::RED);
-    }
-}
-
-TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) {
-    auto transaction = Transaction().show(fgLayer);
-    fgLayer = nullptr;
-    {
-        SCOPED_TRACE("after setting null");
-        auto shot = screenshot();
-        shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE);
-    }
-}
-
-class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
-protected:
-    virtual void SetUp() {
-        LayerTransactionTest::SetUp();
-        ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
-        mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
-        SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
-
-        sp<IGraphicBufferConsumer> consumer;
-        BufferQueue::createBufferQueue(&mProducer, &consumer);
-        consumer->setConsumerName(String8("Virtual disp consumer"));
-        consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
-    }
-
-    virtual void TearDown() {
-        SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
-        LayerTransactionTest::TearDown();
-        mColorLayer = 0;
-    }
-
-    void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
-        mVirtualDisplay =
-                SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
-        asTransaction([&](Transaction& t) {
-            t.setDisplaySurface(mVirtualDisplay, mProducer);
-            t.setDisplayLayerStack(mVirtualDisplay, layerStack);
-            t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
-                                   Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
-        });
-    }
-
-    void createColorLayer(uint32_t layerStack) {
-        mColorLayer =
-                createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
-                              PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
-        ASSERT_TRUE(mColorLayer != nullptr);
-        ASSERT_TRUE(mColorLayer->isValid());
-        asTransaction([&](Transaction& t) {
-            t.setLayerStack(mColorLayer, layerStack);
-            t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
-            t.setLayer(mColorLayer, INT32_MAX - 2);
-            t.setColor(mColorLayer,
-                       half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
-                             mExpectedColor.b / 255.0f});
-            t.show(mColorLayer);
-        });
-    }
-
-    DisplayInfo mMainDisplayInfo;
-    sp<IBinder> mMainDisplay;
-    sp<IBinder> mVirtualDisplay;
-    sp<IGraphicBufferProducer> mProducer;
-    sp<SurfaceControl> mColorLayer;
-    Color mExpectedColor = {63, 63, 195, 255};
-};
-
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
-    createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
-    createColorLayer(1 /* layerStack */);
-
-    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
-
-    // Verify color layer does not render on main display.
-    std::unique_ptr<ScreenCapture> sc;
-    ScreenCapture::captureScreen(&sc, mMainDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
-    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-
-    // Verify color layer renders correctly on virtual display.
-    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
-    sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
-}
-
-TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
-    // Create a display and set its layer stack to the main display's layer stack so
-    // the contents of the main display are mirrored on to the virtual display.
-
-    // Assumption here is that the new mirrored display has the same viewport as the
-    // primary display that it is mirroring.
-    createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
-    createColorLayer(0 /* layerStack */);
-
-    asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
-
-    // Verify color layer renders correctly on main display and it is mirrored on the
-    // virtual display.
-    std::unique_ptr<ScreenCapture> sc;
-    ScreenCapture::captureScreen(&sc, mMainDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
-    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-
-    ScreenCapture::captureScreen(&sc, mVirtualDisplay);
-    sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
-    sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
-}
-
-class DisplayActiveConfigTest : public ::testing::Test {
-protected:
-    void SetUp() override {
-        mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
-        SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs);
-        EXPECT_GT(mDisplayconfigs.size(), 0);
-
-        // set display power to on to make sure config can be changed
-        SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL);
-    }
-
-    sp<IBinder> mDisplayToken;
-    Vector<DisplayInfo> mDisplayconfigs;
-};
-
-TEST_F(DisplayActiveConfigTest, allConfigsAllowed) {
-    std::vector<int32_t> allowedConfigs;
-
-    // Add all configs to the allowed configs
-    for (int i = 0; i < mDisplayconfigs.size(); i++) {
-        allowedConfigs.push_back(i);
-    }
-
-    status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
-    EXPECT_EQ(res, NO_ERROR);
-
-    std::vector<int32_t> outConfigs;
-    res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs);
-    EXPECT_EQ(res, NO_ERROR);
-    EXPECT_EQ(allowedConfigs, outConfigs);
-}
-
-TEST_F(DisplayActiveConfigTest, changeAllowedConfig) {
-    // we need at least 2 configs available for this test
-    if (mDisplayconfigs.size() <= 1) return;
-
-    int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
-
-    // We want to set the allowed config to everything but the active config
-    std::vector<int32_t> allowedConfigs;
-    for (int i = 0; i < mDisplayconfigs.size(); i++) {
-        if (i != activeConfig) {
-            allowedConfigs.push_back(i);
-        }
-    }
-
-    status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
-    EXPECT_EQ(res, NO_ERROR);
-
-    // Allow some time for the config change
-    std::this_thread::sleep_for(200ms);
-
-    int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
-    EXPECT_NE(activeConfig, newActiveConfig);
-
-    // Make sure the new config is part of allowed config
-    EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) !=
-                allowedConfigs.end());
-}
-
-class RelativeZTest : public LayerTransactionTest {
-protected:
-    virtual void SetUp() {
-        LayerTransactionTest::SetUp();
-        ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
-        const auto display = SurfaceComposerClient::getInternalDisplayToken();
-        ASSERT_FALSE(display == nullptr);
-
-        // Back layer
-        mBackgroundLayer = createColorLayer("Background layer", Color::RED);
-
-        // Front layer
-        mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN);
-
-        asTransaction([&](Transaction& t) {
-            t.setDisplayLayerStack(display, 0);
-            t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer);
-            t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer);
-        });
-    }
-
-    virtual void TearDown() {
-        LayerTransactionTest::TearDown();
-        mBackgroundLayer = 0;
-        mForegroundLayer = 0;
-    }
-
-    sp<SurfaceControl> mBackgroundLayer;
-    sp<SurfaceControl> mForegroundLayer;
-};
-
-// When a layer is reparented offscreen, remove relative z order if the relative parent
-// is still onscreen so that the layer is not drawn.
-TEST_F(RelativeZTest, LayerRemoved) {
-    std::unique_ptr<ScreenCapture> sc;
-
-    // Background layer (RED)
-    //   Child layer (WHITE) (relative to foregroud layer)
-    // Foregroud layer (GREEN)
-    sp<SurfaceControl> childLayer =
-            createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get());
-
-    Transaction{}
-            .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1)
-            .show(childLayer)
-            .apply();
-
-    {
-        // The childLayer should be in front of the FG control.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b);
-    }
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    Transaction{}.reparent(childLayer, nullptr).apply();
-
-    // Background layer (RED)
-    //   Child layer (WHITE)
-    // Foregroud layer (GREEN)
-    Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply();
-
-    {
-        // The relative z info for child layer should be reset, leaving FG control on top.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
-    }
-}
-
-// When a layer is reparented offscreen, preseve relative z order if the relative parent
-// is also offscreen. Regression test b/132613412
-TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) {
-    std::unique_ptr<ScreenCapture> sc;
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    //   child level 1 (WHITE)
-    //     child level 2a (BLUE)
-    //       child level 3 (GREEN) (relative to child level 2b)
-    //     child level 2b (BLACK)
-    sp<SurfaceControl> childLevel1 =
-            createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get());
-    sp<SurfaceControl> childLevel2a =
-            createColorLayer("child level 2a", Color::BLUE, childLevel1.get());
-    sp<SurfaceControl> childLevel2b =
-            createColorLayer("child level 2b", Color::BLACK, childLevel1.get());
-    sp<SurfaceControl> childLevel3 =
-            createColorLayer("child level 3", Color::GREEN, childLevel2a.get());
-
-    Transaction{}
-            .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1)
-            .show(childLevel2a)
-            .show(childLevel2b)
-            .show(childLevel3)
-            .apply();
-
-    {
-        // The childLevel3 should be in front of childLevel2b.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
-    }
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    Transaction{}.reparent(childLevel1, nullptr).apply();
-
-    // Background layer (RED)
-    // Foregroud layer (GREEN)
-    //   child level 1 (WHITE)
-    //     child level 2 back (BLUE)
-    //       child level 3 (GREEN) (relative to child level 2b)
-    //     child level 2 front (BLACK)
-    Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply();
-
-    {
-        // Nothing should change at this point since relative z info was preserved.
-        ScreenCapture::captureScreen(&sc);
-        sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b);
-    }
-}
-
-// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge
-// the dropped buffer's damage region into the next buffer's damage region. If
-// we don't do this, we'll report an incorrect damage region to hardware
-// composer, resulting in broken rendering. This test checks the BufferQueue
-// case.
-//
-// Unfortunately, we don't currently have a way to inspect the damage region
-// SurfaceFlinger sends to hardware composer from a test, so this test requires
-// the dev to manually watch the device's screen during the test to spot broken
-// rendering. Because the results can't be automatically verified, this test is
-// marked disabled.
-TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) {
-    const int width = mDisplayWidth;
-    const int height = mDisplayHeight;
-    sp<SurfaceControl> layer;
-    ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height));
-    const auto producer = layer->getIGraphicBufferProducer();
-    const sp<IProducerListener> dummyListener(new DummyProducerListener);
-    IGraphicBufferProducer::QueueBufferOutput queueBufferOutput;
-    ASSERT_EQ(OK,
-              producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput));
-
-    std::map<int, sp<GraphicBuffer>> slotMap;
-    auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) {
-        ASSERT_NE(nullptr, buf);
-        const auto iter = slotMap.find(slot);
-        ASSERT_NE(slotMap.end(), iter);
-        *buf = iter->second;
-    };
-
-    auto dequeue = [&](int* outSlot) {
-        ASSERT_NE(nullptr, outSlot);
-        *outSlot = -1;
-        int slot;
-        sp<Fence> fence;
-        uint64_t age;
-        FrameEventHistoryDelta timestamps;
-        const status_t dequeueResult =
-                producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888,
-                                        GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
-                                        &age, &timestamps);
-        if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
-            sp<GraphicBuffer> newBuf;
-            ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf));
-            ASSERT_NE(nullptr, newBuf.get());
-            slotMap[slot] = newBuf;
-        } else {
-            ASSERT_EQ(OK, dequeueResult);
-        }
-        *outSlot = slot;
-    };
-
-    auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) {
-        IGraphicBufferProducer::QueueBufferInput input(
-                /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN,
-                /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
-                /*transform=*/0, Fence::NO_FENCE);
-        input.setSurfaceDamage(damage);
-        IGraphicBufferProducer::QueueBufferOutput output;
-        ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output));
-    };
-
-    auto fillAndPostBuffers = [&](const Color& color) {
-        int slot1;
-        ASSERT_NO_FATAL_FAILURE(dequeue(&slot1));
-        int slot2;
-        ASSERT_NO_FATAL_FAILURE(dequeue(&slot2));
-
-        sp<GraphicBuffer> buf1;
-        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1));
-        sp<GraphicBuffer> buf2;
-        ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2));
-        fillGraphicBufferColor(buf1, Rect(width, height), color);
-        fillGraphicBufferColor(buf2, Rect(width, height), color);
-
-        const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100);
-        ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime));
-        ASSERT_NO_FATAL_FAILURE(
-                queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)),
-                      displayTime));
-    };
-
-    const auto startTime = systemTime();
-    const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE};
-    int colorIndex = 0;
-    while (nanoseconds_to_seconds(systemTime() - startTime) < 10) {
-        ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()]));
-        std::this_thread::sleep_for(1s);
-    }
-
-    ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU));
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 2feff45..ff403f6 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -10,8 +10,13 @@
     ],
     shared_libs: [
         "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.4",
+        "android.hardware.graphics.composer@2.1-resources",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
         "android.hardware.power@1.3",
         "libbase",
         "libbinder",
@@ -24,18 +29,20 @@
         "liblog",
         "libnativewindow",
         "libsync",
-        "libtimestats_proto",
+        "libtimestats",
         "libui",
         "libutils",
     ],
     static_libs: [
+        "libcompositionengine",
         "libgmock",
+        "libperfetto_client_experimental",
         "librenderengine",
         "libtrace_proto",
     ],
     header_libs: [
-        "android.hardware.graphics.composer@2.1-command-buffer",
-        "android.hardware.graphics.composer@2.1-hal",
+        "android.hardware.graphics.composer@2.4-command-buffer",
+        "android.hardware.graphics.composer@2.4-hal",
         "libsurfaceflinger_headers",
     ],
 }
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index eeb6efe..5cbf2ef 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 //#define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeComposer"
@@ -146,6 +150,7 @@
 
 FakeComposerClient::FakeComposerClient()
       : mEventCallback(nullptr),
+        mEventCallback_2_4(nullptr),
         mCurrentConfig(NULL_DISPLAY_CONFIG),
         mVsyncEnabled(false),
         mLayers(),
@@ -165,6 +170,9 @@
 
 void FakeComposerClient::registerEventCallback(EventCallback* callback) {
     ALOGV("registerEventCallback");
+    LOG_FATAL_IF(mEventCallback_2_4 != nullptr,
+                 "already registered using registerEventCallback_2_4");
+
     mEventCallback = callback;
     if (mEventCallback) {
         mEventCallback->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
@@ -179,12 +187,16 @@
 void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
     if (mEventCallback) {
         mEventCallback->onHotplug(display, state);
+    } else if (mEventCallback_2_4) {
+        mEventCallback_2_4->onHotplug(display, state);
     }
 }
 
 void FakeComposerClient::refreshDisplay(Display display) {
     if (mEventCallback) {
         mEventCallback->onRefresh(display);
+    } else if (mEventCallback_2_4) {
+        mEventCallback_2_4->onRefresh(display);
     }
 }
 
@@ -193,33 +205,37 @@
     return 1;
 }
 
-Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
-                                               PixelFormat* /*format*/, Display* /*outDisplay*/) {
+V2_1::Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
+                                                     V1_0::PixelFormat* /*format*/,
+                                                     Display* /*outDisplay*/) {
     ALOGV("createVirtualDisplay");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
+V2_1::Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
     ALOGV("destroyVirtualDisplay");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
+V2_1::Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
     ALOGV("createLayer");
     *outLayer = mLayers.size();
     auto newLayer = std::make_unique<LayerImpl>();
     mLayers.push_back(std::move(newLayer));
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
+V2_1::Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
     ALOGV("destroyLayer");
     mLayers[layer]->mValid = false;
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) {
+V2_1::Error FakeComposerClient::getActiveConfig(Display display, Config* outConfig) {
     ALOGV("getActiveConfig");
+    if (mMockHal) {
+        return mMockHal->getActiveConfig(display, outConfig);
+    }
 
     // TODO Assert outConfig != nullptr
 
@@ -227,30 +243,480 @@
     // IComposerClient::getActiveConfig, but returning BAD_CONFIG
     // seems to not fit SurfaceFlinger plans. See version 2 below.
     // if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
-    //     return Error::BAD_CONFIG;
+    //     return V2_1::Error::BAD_CONFIG;
     // }
     //*outConfig = mCurrentConfig;
     *outConfig = 1; // Very special config for you my friend
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
-                                                 uint32_t /*height*/, PixelFormat /*format*/,
-                                                 Dataspace /*dataspace*/) {
+V2_1::Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
+                                                       uint32_t /*height*/,
+                                                       V1_0::PixelFormat /*format*/,
+                                                       V1_0::Dataspace /*dataspace*/) {
     ALOGV("getClientTargetSupport");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) {
+V2_1::Error FakeComposerClient::getColorModes(Display /*display*/,
+                                              hidl_vec<V1_0::ColorMode>* /*outModes*/) {
     ALOGV("getColorModes");
-    return Error::NONE;
+    return V2_1::Error::NONE;
 }
 
-Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
-                                              IComposerClient::Attribute attribute,
-                                              int32_t* outValue) {
+V2_1::Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
+                                                    V2_1::IComposerClient::Attribute attribute,
+                                                    int32_t* outValue) {
+    auto tmpError =
+            getDisplayAttribute_2_4(display, config,
+                                    static_cast<IComposerClient::Attribute>(attribute), outValue);
+    return static_cast<V2_1::Error>(tmpError);
+}
+
+V2_1::Error FakeComposerClient::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
+    ALOGV("getDisplayConfigs");
+    if (mMockHal) {
+        return mMockHal->getDisplayConfigs(display, outConfigs);
+    }
+
+    // TODO assert display == 1, outConfigs != nullptr
+
+    outConfigs->resize(1);
+    (*outConfigs)[0] = 1;
+
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
+    ALOGV("getDisplayName");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDisplayType(Display /*display*/,
+                                               IComposerClient::DisplayType* outType) {
+    ALOGV("getDisplayType");
+    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
+    // assumed to be physical?
+    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
+    ALOGV("getDozeSupport");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getHdrCapabilities(Display /*display*/,
+                                                   hidl_vec<V1_0::Hdr>* /*outTypes*/,
+                                                   float* /*outMaxLuminance*/,
+                                                   float* /*outMaxAverageLuminance*/,
+                                                   float* /*outMinLuminance*/) {
+    ALOGV("getHdrCapabilities");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setActiveConfig(Display display, Config config) {
+    ALOGV("setActiveConfig");
+    if (mMockHal) {
+        return mMockHal->setActiveConfig(display, config);
+    }
+    mCurrentConfig = config;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setColorMode(Display /*display*/, V1_0::ColorMode /*mode*/) {
+    ALOGV("setColorMode");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setPowerMode(Display /*display*/,
+                                             V2_1::IComposerClient::PowerMode /*mode*/) {
+    ALOGV("setPowerMode");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setVsyncEnabled(Display /*display*/,
+                                                IComposerClient::Vsync enabled) {
+    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
+    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
+                                                  int32_t /*hint*/) {
+    ALOGV("setColorTransform");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
+                                                int32_t /*acquireFence*/, int32_t /*dataspace*/,
+                                                const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setClientTarget");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
+                                                int32_t /*releaseFence*/) {
+    ALOGV("setOutputBuffer");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::validateDisplay(
+        Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
+        std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
+        uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
+        std::vector<uint32_t>* /*outRequestMasks*/) {
+    ALOGV("validateDisplay");
+    // TODO: Assume touching nothing means All Korrekt!
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
+    ALOGV("acceptDisplayChanges");
+    // Didn't ask for changes because software is omnipotent.
+    return V2_1::Error::NONE;
+}
+
+bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
+    return a->z <= b->z;
+}
+
+V2_1::Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
+                                               std::vector<Layer>* /*outLayers*/,
+                                               std::vector<int32_t>* /*outReleaseFences*/) {
+    ALOGV("presentDisplay");
+    // TODO Leaving layers and their fences out for now. Doing so
+    // means that we've already processed everything. Important to
+    // test that the fences are respected, though. (How?)
+
+    std::unique_ptr<Frame> newFrame(new Frame);
+    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
+        const LayerImpl& layerImpl = *mLayers[layer];
+
+        if (!layerImpl.mValid) continue;
+
+        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
+        newFrame->rectangles.push_back(std::move(rect));
+    }
+    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
+    {
+        Mutex::Autolock _l(mStateMutex);
+        mFrames.push_back(std::move(newFrame));
+        mFramesAvailable.broadcast();
+    }
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
+                                                       int32_t /*x*/, int32_t /*y*/) {
+    ALOGV("setLayerCursorPosition");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer,
+                                               buffer_handle_t buffer, int32_t acquireFence) {
+    ALOGV("setLayerBuffer");
+    LayerImpl& l = getLayerImpl(layer);
+    if (buffer != l.mRenderState.mBuffer) {
+        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
+    }
+    l.mRenderState.mBuffer = buffer;
+    l.mRenderState.mAcquireFence = acquireFence;
+
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
+                                                      const std::vector<hwc_rect_t>& /*damage*/) {
+    ALOGV("setLayerSurfaceDamage");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
+    ALOGV("setLayerBlendMode");
+    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
+                                              IComposerClient::Color color) {
+    ALOGV("setLayerColor");
+    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
+    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
+    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
+    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
+                                                        int32_t /*type*/) {
+    ALOGV("setLayerCompositionType");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
+                                                  int32_t /*dataspace*/) {
+    ALOGV("setLayerDataspace");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
+                                                     const hwc_rect_t& frame) {
+    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
+          frame.bottom);
+    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
+    ALOGV("setLayerPlaneAlpha");
+    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
+                                                       buffer_handle_t /*stream*/) {
+    ALOGV("setLayerSidebandStream");
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
+                                                   const hwc_frect_t& crop) {
+    ALOGV("setLayerSourceCrop");
+    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer,
+                                                  int32_t transform) {
+    ALOGV("setLayerTransform");
+    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
+                                                      const std::vector<hwc_rect_t>& visible) {
+    ALOGV("setLayerVisibleRegion");
+    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
+    return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
+    ALOGV("setLayerZOrder");
+    getLayerImpl(layer).mZ = z;
+    return V2_1::Error::NONE;
+}
+
+// Composer 2.2
+V2_1::Error FakeComposerClient::getPerFrameMetadataKeys(
+        Display /*display*/, std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadata(
+        Display /*display*/, Layer /*layer*/,
+        const std::vector<V2_2::IComposerClient::PerFrameMetadata>& /*metadata*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferAttributes(
+        Display /*display*/, graphics::common::V1_1::PixelFormat* /*outFormat*/,
+        graphics::common::V1_1::Dataspace* /*outDataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setReadbackBuffer(Display /*display*/,
+                                                  const native_handle_t* /*bufferHandle*/,
+                                                  android::base::unique_fd /*fenceFd*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferFence(Display /*display*/,
+                                                       android::base::unique_fd* /*outFenceFd*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::createVirtualDisplay_2_2(
+        uint32_t /*width*/, uint32_t /*height*/, graphics::common::V1_1::PixelFormat* /*format*/,
+        Display* /*outDisplay*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+V2_1::Error FakeComposerClient::getClientTargetSupport_2_2(
+        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
+        graphics::common::V1_1::PixelFormat /*format*/,
+        graphics::common::V1_1::Dataspace /*dataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setPowerMode_2_2(Display /*display*/,
+                                                 V2_2::IComposerClient::PowerMode /*mode*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerFloatColor(Display /*display*/, Layer /*layer*/,
+                                                   V2_2::IComposerClient::FloatColor /*color*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getColorModes_2_2(
+        Display /*display*/, hidl_vec<graphics::common::V1_1::ColorMode>* /*outModes*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getRenderIntents(
+        Display /*display*/, graphics::common::V1_1::ColorMode /*mode*/,
+        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setColorMode_2_2(Display /*display*/,
+                                                 graphics::common::V1_1::ColorMode /*mode*/,
+                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+std::array<float, 16> FakeComposerClient::getDataspaceSaturationMatrix(
+        graphics::common::V1_1::Dataspace /*dataspace*/) {
+    return {};
+}
+
+// Composer 2.3
+V2_1::Error FakeComposerClient::getPerFrameMetadataKeys_2_3(
+        Display /*display*/, std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setColorMode_2_3(Display /*display*/,
+                                                 graphics::common::V1_2::ColorMode /*mode*/,
+                                                 graphics::common::V1_1::RenderIntent /*intent*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getRenderIntents_2_3(
+        Display /*display*/, graphics::common::V1_2::ColorMode /*mode*/,
+        std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getColorModes_2_3(
+        Display /*display*/, hidl_vec<graphics::common::V1_2::ColorMode>* /*outModes*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getClientTargetSupport_2_3(
+        Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
+        graphics::common::V1_2::PixelFormat /*format*/,
+        graphics::common::V1_2::Dataspace /*dataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferAttributes_2_3(
+        Display /*display*/, graphics::common::V1_2::PixelFormat* /*outFormat*/,
+        graphics::common::V1_2::Dataspace* /*outDataspace*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getHdrCapabilities_2_3(
+        Display /*display*/, hidl_vec<graphics::common::V1_2::Hdr>* /*outTypes*/,
+        float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadata_2_3(
+        Display /*display*/, Layer /*layer*/,
+        const std::vector<V2_3::IComposerClient::PerFrameMetadata>& /*metadata*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayIdentificationData(Display /*display*/,
+                                                             uint8_t* /*outPort*/,
+                                                             std::vector<uint8_t>* /*outData*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerColorTransform(Display /*display*/, Layer /*layer*/,
+                                                       const float* /*matrix*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayedContentSamplingAttributes(
+        uint64_t /*display*/, graphics::common::V1_2::PixelFormat& /*format*/,
+        graphics::common::V1_2::Dataspace& /*dataspace*/,
+        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& /*componentMask*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setDisplayedContentSamplingEnabled(
+        uint64_t /*display*/, V2_3::IComposerClient::DisplayedContentSampling /*enable*/,
+        hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> /*componentMask*/,
+        uint64_t /*maxFrames*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayedContentSample(
+        uint64_t /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/,
+        uint64_t& /*frameCount*/, hidl_vec<uint64_t>& /*sampleComponent0*/,
+        hidl_vec<uint64_t>& /*sampleComponent1*/, hidl_vec<uint64_t>& /*sampleComponent2*/,
+        hidl_vec<uint64_t>& /*sampleComponent3*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayCapabilities(
+        Display /*display*/,
+        std::vector<V2_3::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadataBlobs(
+        Display /*display*/, Layer /*layer*/,
+        std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& /*blobs*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayBrightnessSupport(Display /*display*/,
+                                                            bool* /*outSupport*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setDisplayBrightness(Display /*display*/, float /*brightness*/) {
+    return V2_1::Error::UNSUPPORTED;
+}
+
+// Composer 2.4
+void FakeComposerClient::registerEventCallback_2_4(EventCallback_2_4* callback) {
+    ALOGV("registerEventCallback_2_4");
+    LOG_FATAL_IF(mEventCallback != nullptr, "already registered using registerEventCallback");
+
+    mEventCallback_2_4 = callback;
+    if (mEventCallback_2_4) {
+        mEventCallback_2_4->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+    }
+}
+
+void FakeComposerClient::unregisterEventCallback_2_4() {
+    ALOGV("unregisterEventCallback_2_4");
+    mEventCallback_2_4 = nullptr;
+}
+
+V2_4::Error FakeComposerClient::getDisplayCapabilities_2_4(
+        Display /*display*/,
+        std::vector<V2_4::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
+    return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getDisplayConnectionType(
+        Display /*display*/, V2_4::IComposerClient::DisplayConnectionType* /*outType*/) {
+    return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getDisplayAttribute_2_4(Display display, Config config,
+                                                        IComposerClient::Attribute attribute,
+                                                        int32_t* outValue) {
     ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
           static_cast<int>(config), static_cast<int>(attribute), outValue);
+    if (mMockHal) {
+        return mMockHal->getDisplayAttribute_2_4(display, config, attribute, outValue);
+    }
 
     // TODO: SOOO much fun to be had with these alone
     switch (attribute) {
@@ -276,233 +742,69 @@
     return Error::NONE;
 }
 
-Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) {
-    ALOGV("getDisplayConfigs");
-    // TODO assert display == 1, outConfigs != nullptr
+V2_4::Error FakeComposerClient::getDisplayVsyncPeriod(Display display,
+                                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) {
+    ALOGV("getDisplayVsyncPeriod");
+    if (mMockHal) {
+        return mMockHal->getDisplayVsyncPeriod(display, outVsyncPeriod);
+    }
 
-    outConfigs->resize(1);
-    (*outConfigs)[0] = 1;
-
-    return Error::NONE;
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
-    ALOGV("getDisplayName");
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setActiveConfigWithConstraints(
+        Display display, Config config,
+        const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+        VsyncPeriodChangeTimeline* timeline) {
+    ALOGV("setActiveConfigWithConstraints");
+    if (mMockHal) {
+        return mMockHal->setActiveConfigWithConstraints(display, config,
+                                                        vsyncPeriodChangeConstraints, timeline);
+    }
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getDisplayType(Display /*display*/,
-                                         IComposerClient::DisplayType* outType) {
-    ALOGV("getDisplayType");
-    // TODO: This setting nothing on the output had no effect on initial trials. Is first display
-    // assumed to be physical?
-    *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
+    ALOGV("setAutoLowLatencyMode");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
-    ALOGV("getDozeSupport");
-    return Error::NONE;
+V2_4::Error FakeComposerClient::getSupportedContentTypes(
+        Display, std::vector<IComposerClient::ContentType>*) {
+    ALOGV("getSupportedContentTypes");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/,
-                                             float* /*outMaxLuminance*/,
-                                             float* /*outMaxAverageLuminance*/,
-                                             float* /*outMinLuminance*/) {
-    ALOGV("getHdrCapabilities");
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
+    ALOGV("setContentType");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) {
-    ALOGV("setActiveConfig");
-    mCurrentConfig = config;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) {
-    ALOGV("setColorMode");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) {
-    ALOGV("setPowerMode");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) {
-    mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
-    ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
-                                            int32_t /*hint*/) {
-    ALOGV("setColorTransform");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
-                                          int32_t /*acquireFence*/, int32_t /*dataspace*/,
-                                          const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setClientTarget");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
-                                          int32_t /*releaseFence*/) {
-    ALOGV("setOutputBuffer");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::validateDisplay(
+V2_4::Error FakeComposerClient::validateDisplay_2_4(
         Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
         std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
         uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
-        std::vector<uint32_t>* /*outRequestMasks*/) {
-    ALOGV("validateDisplay");
-    // TODO: Assume touching nothing means All Korrekt!
-    return Error::NONE;
+        std::vector<uint32_t>* /*outRequestMasks*/,
+        IComposerClient::ClientTargetProperty* /*outClientTargetProperty*/) {
+    return V2_4::Error::NONE;
 }
 
-Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
-    ALOGV("acceptDisplayChanges");
-    // Didn't ask for changes because software is omnipotent.
-    return Error::NONE;
+V2_4::Error FakeComposerClient::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
+                                                        const std::vector<uint8_t>&) {
+    ALOGV("setLayerGenericMetadata");
+    return V2_4::Error::UNSUPPORTED;
 }
 
-bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
-    return a->z <= b->z;
-}
-
-Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
-                                         std::vector<Layer>* /*outLayers*/,
-                                         std::vector<int32_t>* /*outReleaseFences*/) {
-    ALOGV("presentDisplay");
-    // TODO Leaving layers and their fences out for now. Doing so
-    // means that we've already processed everything. Important to
-    // test that the fences are respected, though. (How?)
-
-    std::unique_ptr<Frame> newFrame(new Frame);
-    for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
-        const LayerImpl& layerImpl = *mLayers[layer];
-
-        if (!layerImpl.mValid) continue;
-
-        auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
-        newFrame->rectangles.push_back(std::move(rect));
-    }
-    std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
-    {
-        Mutex::Autolock _l(mStateMutex);
-        mFrames.push_back(std::move(newFrame));
-        mFramesAvailable.broadcast();
-    }
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
-                                                 int32_t /*x*/, int32_t /*y*/) {
-    ALOGV("setLayerCursorPosition");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer,
-                                         int32_t acquireFence) {
-    ALOGV("setLayerBuffer");
-    LayerImpl& l = getLayerImpl(layer);
-    if (buffer != l.mRenderState.mBuffer) {
-        l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
-    }
-    l.mRenderState.mBuffer = buffer;
-    l.mRenderState.mAcquireFence = acquireFence;
-
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
-                                                const std::vector<hwc_rect_t>& /*damage*/) {
-    ALOGV("setLayerSurfaceDamage");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
-    ALOGV("setLayerBlendMode");
-    getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
-                                        IComposerClient::Color color) {
-    ALOGV("setLayerColor");
-    getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
-    getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
-    getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
-    getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
-                                                  int32_t /*type*/) {
-    ALOGV("setLayerCompositionType");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
-                                            int32_t /*dataspace*/) {
-    ALOGV("setLayerDataspace");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
-                                               const hwc_rect_t& frame) {
-    ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
-          frame.bottom);
-    getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
-    ALOGV("setLayerPlaneAlpha");
-    getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
-                                                 buffer_handle_t /*stream*/) {
-    ALOGV("setLayerSidebandStream");
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
-                                             const hwc_frect_t& crop) {
-    ALOGV("setLayerSourceCrop");
-    getLayerImpl(layer).mRenderState.mSourceCrop = crop;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) {
-    ALOGV("setLayerTransform");
-    getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
-                                                const std::vector<hwc_rect_t>& visible) {
-    ALOGV("setLayerVisibleRegion");
-    getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
-    return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
-    ALOGV("setLayerZOrder");
-    getLayerImpl(layer).mZ = z;
-    return Error::NONE;
+V2_4::Error FakeComposerClient::getLayerGenericMetadataKeys(
+        std::vector<IComposerClient::LayerGenericMetadataKey>*) {
+    ALOGV("getLayerGenericMetadataKeys");
+    return V2_4::Error::UNSUPPORTED;
 }
 
 //////////////////////////////////////////////////////////////////
 
 void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
-    if (mEventCallback) {
+    if (mEventCallback || mEventCallback_2_4) {
         uint64_t timestamp = vsyncTime;
         ALOGV("Vsync");
         if (timestamp == 0) {
@@ -512,8 +814,10 @@
         }
         if (mSurfaceComposer != nullptr) {
             mSurfaceComposer->injectVSync(timestamp);
-        } else {
+        } else if (mEventCallback) {
             mEventCallback->onVsync(PRIMARY_DISPLAY, timestamp);
+        } else {
+            mEventCallback_2_4->onVsync_2_4(PRIMARY_DISPLAY, timestamp, 16'666'666);
         }
     }
 }
@@ -617,3 +921,6 @@
     // this might get more involving.
     return static_cast<Layer>(index);
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index d115d79..5240b72 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -19,10 +19,15 @@
 #define HWC2_USE_CPP11
 #define HWC2_INCLUDE_STRINGIFICATION
 #include <composer-hal/2.1/ComposerClient.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
+#include <composer-hal/2.4/ComposerClient.h>
 #undef HWC2_USE_CPP11
 #undef HWC2_INCLUDE_STRINGIFICATION
 #include "RenderState.h"
 
+#include "MockComposerHal.h"
+
 // Needed for display type/ID enums
 #include <hardware/hwcomposer_defs.h>
 
@@ -30,8 +35,10 @@
 
 #include <chrono>
 
-using namespace android::hardware::graphics::composer::V2_1;
-using namespace android::hardware::graphics::composer::V2_1::hal;
+using namespace android::hardware::graphics::common;
+using namespace android::hardware::graphics::composer;
+using namespace android::hardware::graphics::composer::V2_4;
+using namespace android::hardware::graphics::composer::V2_4::hal;
 using namespace android::hardware;
 using namespace std::chrono_literals;
 
@@ -46,7 +53,6 @@
 } // namespace android
 
 namespace sftest {
-
 // NOTE: The ID's need to be exactly these. VR composer and parts of
 // the SurfaceFlinger assume the display IDs to have these values
 // despite the enum being documented as a display type.
@@ -59,6 +65,8 @@
     FakeComposerClient();
     virtual ~FakeComposerClient();
 
+    void setMockHal(MockComposerHal* mockHal) { mMockHal = mockHal; }
+
     bool hasCapability(hwc2_capability_t capability) override;
 
     std::string dumpDebugInfo() override;
@@ -66,59 +74,193 @@
     void unregisterEventCallback() override;
 
     uint32_t getMaxVirtualDisplayCount() override;
-    Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
-                               Display* outDisplay) override;
-    Error destroyVirtualDisplay(Display display) override;
-    Error createLayer(Display display, Layer* outLayer) override;
-    Error destroyLayer(Display display, Layer layer) override;
+    V2_1::Error createVirtualDisplay(uint32_t width, uint32_t height, V1_0::PixelFormat* format,
+                                     Display* outDisplay) override;
+    V2_1::Error destroyVirtualDisplay(Display display) override;
+    V2_1::Error createLayer(Display display, Layer* outLayer) override;
+    V2_1::Error destroyLayer(Display display, Layer layer) override;
 
-    Error getActiveConfig(Display display, Config* outConfig) override;
-    Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
-                                 PixelFormat format, Dataspace dataspace) override;
-    Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
-    Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
-                              int32_t* outValue) override;
-    Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
-    Error getDisplayName(Display display, hidl_string* outName) override;
-    Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
-    Error getDozeSupport(Display display, bool* outSupport) override;
-    Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance,
-                             float* outMaxAverageLuminance, float* outMinLuminance) override;
+    V2_1::Error getActiveConfig(Display display, Config* outConfig) override;
+    V2_1::Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
+                                       V1_0::PixelFormat format,
+                                       V1_0::Dataspace dataspace) override;
+    V2_1::Error getColorModes(Display display, hidl_vec<V1_0::ColorMode>* outModes) override;
+    V2_1::Error getDisplayAttribute(Display display, Config config,
+                                    V2_1::IComposerClient::Attribute attribute,
+                                    int32_t* outValue) override;
+    V2_1::Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+    V2_1::Error getDisplayName(Display display, hidl_string* outName) override;
+    V2_1::Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
+    V2_1::Error getDozeSupport(Display display, bool* outSupport) override;
+    V2_1::Error getHdrCapabilities(Display display, hidl_vec<V1_0::Hdr>* outTypes,
+                                   float* outMaxLuminance, float* outMaxAverageLuminance,
+                                   float* outMinLuminance) override;
 
-    Error setActiveConfig(Display display, Config config) override;
-    Error setColorMode(Display display, ColorMode mode) override;
-    Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
-    Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+    V2_1::Error setActiveConfig(Display display, Config config) override;
+    V2_1::Error setColorMode(Display display, V1_0::ColorMode mode) override;
+    V2_1::Error setPowerMode(Display display, V2_1::IComposerClient::PowerMode mode) override;
+    V2_1::Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
 
-    Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
-    Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
-                          int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
-    Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override;
-    Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
-                          std::vector<IComposerClient::Composition>* outCompositionTypes,
-                          uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
-                          std::vector<uint32_t>* outRequestMasks) override;
-    Error acceptDisplayChanges(Display display) override;
-    Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers,
-                         std::vector<int32_t>* outReleaseFences) override;
+    V2_1::Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
+    V2_1::Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
+                                int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
+    V2_1::Error setOutputBuffer(Display display, buffer_handle_t buffer,
+                                int32_t releaseFence) override;
+    V2_1::Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
+                                std::vector<IComposerClient::Composition>* outCompositionTypes,
+                                uint32_t* outDisplayRequestMask,
+                                std::vector<Layer>* outRequestedLayers,
+                                std::vector<uint32_t>* outRequestMasks) override;
+    V2_1::Error acceptDisplayChanges(Display display) override;
+    V2_1::Error presentDisplay(Display display, int32_t* outPresentFence,
+                               std::vector<Layer>* outLayers,
+                               std::vector<int32_t>* outReleaseFences) override;
 
-    Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
-    Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
-                         int32_t acquireFence) override;
-    Error setLayerSurfaceDamage(Display display, Layer layer,
-                                const std::vector<hwc_rect_t>& damage) override;
-    Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
-    Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
-    Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
-    Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
-    Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override;
-    Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
-    Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override;
-    Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
-    Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
-    Error setLayerVisibleRegion(Display display, Layer layer,
-                                const std::vector<hwc_rect_t>& visible) override;
-    Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+    V2_1::Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+    V2_1::Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+                               int32_t acquireFence) override;
+    V2_1::Error setLayerSurfaceDamage(Display display, Layer layer,
+                                      const std::vector<hwc_rect_t>& damage) override;
+    V2_1::Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+    V2_1::Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
+    V2_1::Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
+    V2_1::Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
+    V2_1::Error setLayerDisplayFrame(Display display, Layer layer,
+                                     const hwc_rect_t& frame) override;
+    V2_1::Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+    V2_1::Error setLayerSidebandStream(Display display, Layer layer,
+                                       buffer_handle_t stream) override;
+    V2_1::Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
+    V2_1::Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
+    V2_1::Error setLayerVisibleRegion(Display display, Layer layer,
+                                      const std::vector<hwc_rect_t>& visible) override;
+    V2_1::Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+    // Composer 2.2
+    V2_1::Error getPerFrameMetadataKeys(
+            Display display,
+            std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* outKeys) override;
+    V2_1::Error setLayerPerFrameMetadata(
+            Display display, Layer layer,
+            const std::vector<V2_2::IComposerClient::PerFrameMetadata>& metadata) override;
+
+    V2_1::Error getReadbackBufferAttributes(
+            Display display, graphics::common::V1_1::PixelFormat* outFormat,
+            graphics::common::V1_1::Dataspace* outDataspace) override;
+    V2_1::Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
+                                  android::base::unique_fd fenceFd) override;
+    V2_1::Error getReadbackBufferFence(Display display,
+                                       android::base::unique_fd* outFenceFd) override;
+    V2_1::Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+                                         graphics::common::V1_1::PixelFormat* format,
+                                         Display* outDisplay) override;
+    V2_1::Error getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
+                                           graphics::common::V1_1::PixelFormat format,
+                                           graphics::common::V1_1::Dataspace dataspace) override;
+    V2_1::Error setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) override;
+
+    V2_1::Error setLayerFloatColor(Display display, Layer layer,
+                                   V2_2::IComposerClient::FloatColor color) override;
+
+    V2_1::Error getColorModes_2_2(Display display,
+                                  hidl_vec<graphics::common::V1_1::ColorMode>* outModes) override;
+    V2_1::Error getRenderIntents(
+            Display display, graphics::common::V1_1::ColorMode mode,
+            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
+    V2_1::Error setColorMode_2_2(Display display, graphics::common::V1_1::ColorMode mode,
+                                 graphics::common::V1_1::RenderIntent intent) override;
+
+    std::array<float, 16> getDataspaceSaturationMatrix(
+            graphics::common::V1_1::Dataspace dataspace) override;
+
+    // Composer 2.3
+    V2_1::Error getPerFrameMetadataKeys_2_3(
+            Display display,
+            std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* outKeys) override;
+
+    V2_1::Error setColorMode_2_3(Display display, graphics::common::V1_2::ColorMode mode,
+                                 graphics::common::V1_1::RenderIntent intent) override;
+
+    V2_1::Error getRenderIntents_2_3(
+            Display display, graphics::common::V1_2::ColorMode mode,
+            std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
+
+    V2_1::Error getColorModes_2_3(Display display,
+                                  hidl_vec<graphics::common::V1_2::ColorMode>* outModes) override;
+
+    V2_1::Error getClientTargetSupport_2_3(Display display, uint32_t width, uint32_t height,
+                                           graphics::common::V1_2::PixelFormat format,
+                                           graphics::common::V1_2::Dataspace dataspace) override;
+    V2_1::Error getReadbackBufferAttributes_2_3(
+            Display display, graphics::common::V1_2::PixelFormat* outFormat,
+            graphics::common::V1_2::Dataspace* outDataspace) override;
+    V2_1::Error getHdrCapabilities_2_3(Display display,
+                                       hidl_vec<graphics::common::V1_2::Hdr>* outTypes,
+                                       float* outMaxLuminance, float* outMaxAverageLuminance,
+                                       float* outMinLuminance) override;
+    V2_1::Error setLayerPerFrameMetadata_2_3(
+            Display display, Layer layer,
+            const std::vector<V2_3::IComposerClient::PerFrameMetadata>& metadata) override;
+    V2_1::Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                             std::vector<uint8_t>* outData) override;
+    V2_1::Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+    V2_1::Error getDisplayedContentSamplingAttributes(
+            uint64_t display, graphics::common::V1_2::PixelFormat& format,
+            graphics::common::V1_2::Dataspace& dataspace,
+            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& componentMask) override;
+    V2_1::Error setDisplayedContentSamplingEnabled(
+            uint64_t display, V2_3::IComposerClient::DisplayedContentSampling enable,
+            hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> componentMask,
+            uint64_t maxFrames) override;
+    V2_1::Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
+                                          uint64_t& frameCount,
+                                          hidl_vec<uint64_t>& sampleComponent0,
+                                          hidl_vec<uint64_t>& sampleComponent1,
+                                          hidl_vec<uint64_t>& sampleComponent2,
+                                          hidl_vec<uint64_t>& sampleComponent3) override;
+    V2_1::Error getDisplayCapabilities(
+            Display display,
+            std::vector<V2_3::IComposerClient::DisplayCapability>* outCapabilities) override;
+    V2_1::Error setLayerPerFrameMetadataBlobs(
+            Display display, Layer layer,
+            std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& blobs) override;
+    V2_1::Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
+    V2_1::Error setDisplayBrightness(Display display, float brightness) override;
+
+    // Composer 2.4
+    void registerEventCallback_2_4(EventCallback_2_4* callback) override;
+
+    void unregisterEventCallback_2_4() override;
+
+    V2_4::Error getDisplayCapabilities_2_4(
+            Display display,
+            std::vector<V2_4::IComposerClient::DisplayCapability>* outCapabilities) override;
+    V2_4::Error getDisplayConnectionType(
+            Display display, V2_4::IComposerClient::DisplayConnectionType* outType) override;
+    V2_4::Error getDisplayAttribute_2_4(Display display, Config config,
+                                        IComposerClient::Attribute attribute,
+                                        int32_t* outValue) override;
+    V2_4::Error getDisplayVsyncPeriod(Display display,
+                                      V2_4::VsyncPeriodNanos* outVsyncPeriod) override;
+    V2_4::Error setActiveConfigWithConstraints(
+            Display display, Config config,
+            const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            VsyncPeriodChangeTimeline* outTimeline) override;
+    V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
+    V2_4::Error getSupportedContentTypes(
+            Display display,
+            std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+    V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
+    V2_4::Error validateDisplay_2_4(
+            Display display, std::vector<Layer>* outChangedLayers,
+            std::vector<IComposerClient::Composition>* outCompositionTypes,
+            uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+            std::vector<uint32_t>* outRequestMasks,
+            IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
+    V2_4::Error setLayerGenericMetadata(Display display, Layer layer, const std::string& key,
+                                        bool mandatory, const std::vector<uint8_t>& value) override;
+    V2_4::Error getLayerGenericMetadataKeys(
+            std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
 
     void setClient(ComposerClient* client);
 
@@ -150,6 +292,7 @@
     LayerImpl& getLayerImpl(Layer handle);
 
     EventCallback* mEventCallback;
+    EventCallback_2_4* mEventCallback_2_4;
     Config mCurrentConfig;
     bool mVsyncEnabled;
     std::vector<std::unique_ptr<LayerImpl>> mLayers;
@@ -159,6 +302,8 @@
     android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
     mutable android::Mutex mStateMutex;
     mutable android::Condition mFramesAvailable;
+
+    MockComposerHal* mMockHal = nullptr;
 };
 
 } // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
index f70cbdb..c656eed 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeHwcService"
@@ -22,35 +26,154 @@
 #include "FakeComposerService.h"
 
 using namespace android::hardware;
+using namespace android::hardware::graphics::composer;
 
 namespace sftest {
 
-FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {}
+FakeComposerService_2_1::FakeComposerService_2_1(android::sp<ComposerClient>& client)
+      : mClient(client) {}
 
-FakeComposerService::~FakeComposerService() {
+FakeComposerService_2_1::~FakeComposerService_2_1() {
     ALOGI("Maybe killing client %p", mClient.get());
     // Rely on sp to kill the client.
 }
 
-Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::getCapabilities(getCapabilities_cb hidl_cb) {
     ALOGI("FakeComposerService::getCapabilities");
     hidl_cb(hidl_vec<Capability>());
     return Void();
 }
 
-Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
     ALOGI("FakeComposerService::dumpDebugInfo");
     hidl_cb(hidl_string());
     return Void();
 }
 
-Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::createClient(createClient_cb hidl_cb) {
     ALOGI("FakeComposerService::createClient %p", mClient.get());
     if (!mClient->init()) {
         LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
     }
-    hidl_cb(Error::NONE, mClient);
+    hidl_cb(V2_1::Error::NONE, mClient);
+    return Void();
+}
+
+FakeComposerService_2_2::FakeComposerService_2_2(android::sp<ComposerClient>& client)
+      : mClient(client) {}
+
+FakeComposerService_2_2::~FakeComposerService_2_2() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_2::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_2::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_2::createClient(createClient_cb hidl_cb) {
+    ALOGI("FakeComposerService::createClient %p", mClient.get());
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::NONE, mClient);
+    return Void();
+}
+
+FakeComposerService_2_3::FakeComposerService_2_3(android::sp<ComposerClient>& client)
+      : mClient(client) {}
+
+FakeComposerService_2_3::~FakeComposerService_2_3() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_3::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_3::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_3::createClient(createClient_cb hidl_cb) {
+    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_3");
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+    return Void();
+}
+
+Return<void> FakeComposerService_2_3::createClient_2_3(createClient_2_3_cb hidl_cb) {
+    ALOGI("FakeComposerService_2_3::createClient_2_3 %p", mClient.get());
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::NONE, mClient);
+    return Void();
+}
+
+FakeComposerService_2_4::FakeComposerService_2_4(android::sp<ComposerClient>& client)
+      : mClient(client) {}
+
+FakeComposerService_2_4::~FakeComposerService_2_4() {
+    ALOGI("Maybe killing client %p", mClient.get());
+    // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_4::getCapabilities(getCapabilities_cb hidl_cb) {
+    ALOGI("FakeComposerService::getCapabilities");
+    hidl_cb(hidl_vec<Capability>());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+    ALOGI("FakeComposerService::dumpDebugInfo");
+    hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient(createClient_cb hidl_cb) {
+    LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_4");
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient_2_3(createClient_2_3_cb hidl_cb) {
+    LOG_ALWAYS_FATAL("createClient_2_3 called on FakeComposerService_2_4");
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+    return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient_2_4(createClient_2_4_cb hidl_cb) {
+    ALOGI("FakeComposerService_2_4::createClient_2_4 %p", mClient.get());
+    if (!mClient->init()) {
+        LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+    }
+    hidl_cb(V2_4::Error::NONE, mClient);
     return Void();
 }
 
 } // namespace sftest
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
index a3fb8a6..47f970f 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -16,18 +16,24 @@
 
 #pragma once
 
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
 #include <composer-hal/2.1/ComposerClient.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
+#include <composer-hal/2.4/ComposerClient.h>
 
-using namespace android::hardware::graphics::composer::V2_1;
-using namespace android::hardware::graphics::composer::V2_1::hal;
 using android::hardware::Return;
 
+using ComposerClient = android::hardware::graphics::composer::V2_4::hal::ComposerClient;
+
 namespace sftest {
 
-class FakeComposerService : public IComposer {
+using IComposer_2_1 = android::hardware::graphics::composer::V2_1::IComposer;
+
+class FakeComposerService_2_1 : public IComposer_2_1 {
 public:
-    explicit FakeComposerService(android::sp<ComposerClient>& client);
-    virtual ~FakeComposerService();
+    explicit FakeComposerService_2_1(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_1();
 
     Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
     Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
@@ -37,4 +43,50 @@
     android::sp<ComposerClient> mClient;
 };
 
+using IComposer_2_2 = android::hardware::graphics::composer::V2_2::IComposer;
+class FakeComposerService_2_2 : public IComposer_2_2 {
+public:
+    explicit FakeComposerService_2_2(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_2();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
+using IComposer_2_3 = android::hardware::graphics::composer::V2_3::IComposer;
+class FakeComposerService_2_3 : public IComposer_2_3 {
+public:
+    explicit FakeComposerService_2_3(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_3();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
+using IComposer_2_4 = android::hardware::graphics::composer::V2_4::IComposer;
+
+class FakeComposerService_2_4 : public IComposer_2_4 {
+public:
+    explicit FakeComposerService_2_4(android::sp<ComposerClient>& client);
+    virtual ~FakeComposerService_2_4();
+
+    Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+    Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+    Return<void> createClient(createClient_cb hidl_cb) override;
+    Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
+    Return<void> createClient_2_4(createClient_2_4_cb hidl_cb) override;
+
+private:
+    android::sp<ComposerClient> mClient;
+};
+
 } // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
index 51956ec..96a7541 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeHwcUtil"
@@ -167,7 +171,9 @@
     }
     // TODO: Try registering the mock as the default service instead.
     property_set("debug.sf.hwc_service_name", "mock");
-    // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files.
+
+    // This allows tests/SF to register/load a HIDL service not listed in manifest files.
+    setenv("TREBLE_TESTING_OVERRIDE", "true", true);
     property_set("debug.sf.treble_testing_override", "true");
 }
 
@@ -177,7 +183,11 @@
     // Wait for mock call signaling teardown?
     property_set("debug.sf.nobootanimation", "0");
     property_set("debug.sf.hwc_service_name", "default");
+    system("setenforce 1");
     ALOGI("Test env tear down - done");
 }
 
 } // namespace sftest
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
index 7d20d3c..09a2a89 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -108,9 +108,9 @@
         LOG_ALWAYS_FATAL_IF(android::NO_ERROR != apply());
         // Make sure that exactly one frame has been rendered.
         mComposer.waitUntilFrame(frameCount + 1);
-        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
-                            "Unexpected frame advance. Delta: %d",
-                            mComposer.getFrameCount() - frameCount);
+        //        LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
+        //                            "Unexpected frame advance. Delta: %d",
+        //                            mComposer.getFrameCount() - frameCount);
     }
 
     FakeComposerClient& mComposer;
diff --git a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
new file mode 100644
index 0000000..5dc3778
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <composer-hal/2.4/ComposerClient.h>
+
+#include <gmock/gmock.h>
+
+using namespace android::hardware::graphics::common;
+using namespace android::hardware::graphics::composer;
+using namespace android::hardware::graphics::composer::V2_4;
+using namespace android::hardware::graphics::composer::V2_4::hal;
+using namespace android::hardware;
+using namespace std::chrono_literals;
+
+namespace sftest {
+
+// Mock class for ComposerHal. Implements only the functions used in the test.
+class MockComposerHal {
+public:
+    MOCK_METHOD2(getActiveConfig, V2_1::Error(Display, Config*));
+    MOCK_METHOD4(getDisplayAttribute_2_4,
+                 V2_4::Error(Display, Config, V2_4::IComposerClient::Attribute, int32_t*));
+    MOCK_METHOD2(getDisplayConfigs, V2_1::Error(Display, hidl_vec<Config>*));
+    MOCK_METHOD2(setActiveConfig, V2_1::Error(Display, Config));
+    MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, V2_4::VsyncPeriodNanos*));
+    MOCK_METHOD4(setActiveConfigWithConstraints,
+                 V2_4::Error(Display, Config,
+                             const V2_4::IComposerClient::VsyncPeriodChangeConstraints&,
+                             VsyncPeriodChangeTimeline*));
+};
+
+} // namespace sftest
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index a892a2a..32c58ad 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 // #define LOG_NDEBUG 0
 #undef LOG_TAG
 #define LOG_TAG "FakeHwcTest"
@@ -21,6 +25,7 @@
 #include "FakeComposerClient.h"
 #include "FakeComposerService.h"
 #include "FakeComposerUtils.h"
+#include "MockComposerHal.h"
 
 #include <gui/DisplayEventReceiver.h>
 #include <gui/ISurfaceComposer.h>
@@ -36,13 +41,14 @@
 #include <hwbinder/ProcessState.h>
 #include <log/log.h>
 #include <private/gui/ComposerService.h>
-#include <ui/DisplayInfo.h>
+#include <ui/DisplayConfig.h>
 #include <utils/Looper.h>
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <limits>
+#include <thread>
 
 using namespace std::chrono_literals;
 
@@ -55,12 +61,14 @@
 
 // Mock test helpers
 using ::testing::_;
+using ::testing::AtLeast;
 using ::testing::DoAll;
 using ::testing::Invoke;
 using ::testing::Return;
 using ::testing::SetArgPointee;
 
 using Transaction = SurfaceComposerClient::Transaction;
+using Attribute = V2_4::IComposerClient::Attribute;
 
 ///////////////////////////////////////////////
 
@@ -121,319 +129,1258 @@
                           static_cast<int>(bottom));
 }
 
-////////////////////////////////////////////////
-
+///////////////////////////////////////////////
+template <typename FakeComposerService>
 class DisplayTest : public ::testing::Test {
-public:
-    class MockComposerClient : public FakeComposerClient {
-    public:
-        MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType));
-        MOCK_METHOD4(getDisplayAttribute,
-                     Error(Display display, Config config, IComposerClient::Attribute attribute,
-                           int32_t* outValue));
-
-        // Re-routing to basic fake implementation
-        Error getDisplayAttributeFake(Display display, Config config,
-                                      IComposerClient::Attribute attribute, int32_t* outValue) {
-            return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
-        }
+protected:
+    struct TestConfig {
+        int32_t id;
+        int32_t w;
+        int32_t h;
+        int32_t vsyncPeriod;
+        int32_t group;
     };
 
-protected:
-    static int processDisplayEvents(int fd, int events, void* data);
+    static int processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
+        auto self = static_cast<DisplayTest*>(data);
 
-    void SetUp() override;
-    void TearDown() override;
+        ssize_t n;
+        DisplayEventReceiver::Event buffer[1];
 
-    void waitForDisplayTransaction();
-    bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected);
-
-    sp<IComposer> mFakeService;
-    sp<SurfaceComposerClient> mComposerClient;
-
-    MockComposerClient* mMockComposer;
-
-    std::unique_ptr<DisplayEventReceiver> mReceiver;
-    sp<Looper> mLooper;;
-    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
-};
-
-void DisplayTest::SetUp() {
-    // TODO: The mMockComposer should be a unique_ptr, but it needs to
-    // outlive the test class.  Currently ComposerClient only dies
-    // when the service is replaced. The Mock deletes itself when
-    // removeClient is called on it, which is ugly.  This can be
-    // changed if HIDL ServiceManager allows removing services or
-    // ComposerClient starts taking the ownership of the contained
-    // implementation class. Moving the fake class to the HWC2
-    // interface instead of the current Composer interface might also
-    // change the situation.
-    mMockComposer = new MockComposerClient;
-    sp<ComposerClient> client = new ComposerClient(mMockComposer);
-    mFakeService = new FakeComposerService(client);
-    (void)mFakeService->registerAsService("mock");
-
-    android::hardware::ProcessState::self()->startThreadPool();
-    android::ProcessState::self()->startThreadPool();
-
-    EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
-            .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
-                            Return(Error::NONE)));
-    // Primary display will be queried twice for all 5 attributes. One
-    // set of queries comes from the SurfaceFlinger proper an the
-    // other set from the VR composer.
-    // TODO: Is VR composer always present? Change to atLeast(5)?
-    EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
-            .Times(2 * 5)
-            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-
-    startSurfaceFlinger();
-
-    // Fake composer wants to enable VSync injection
-    mMockComposer->onSurfaceFlingerStart();
-
-    mComposerClient = new SurfaceComposerClient;
-    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-    mReceiver.reset(new DisplayEventReceiver());
-    mLooper = new Looper(false);
-    mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
-}
-
-void DisplayTest::TearDown() {
-    mLooper = nullptr;
-    mReceiver = nullptr;
-
-    mComposerClient->dispose();
-    mComposerClient = nullptr;
-
-    // Fake composer needs to release SurfaceComposerClient before the stop.
-    mMockComposer->onSurfaceFlingerStop();
-    stopSurfaceFlinger();
-
-    mFakeService = nullptr;
-    // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
-    // management.
-    mMockComposer = nullptr;
-}
-
-
-int DisplayTest::processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
-    auto self = static_cast<DisplayTest*>(data);
-
-    ssize_t n;
-    DisplayEventReceiver::Event buffer[1];
-
-    while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
-        for (int i=0 ; i<n ; i++) {
-            self->mReceivedDisplayEvents.push_back(buffer[i]);
+        while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
+            for (int i = 0; i < n; i++) {
+                self->mReceivedDisplayEvents.push_back(buffer[i]);
+            }
         }
+        ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
+        return 1;
     }
-    ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
-    return 1;
-}
 
-void DisplayTest::waitForDisplayTransaction() {
-    // Both a refresh and a vsync event are needed to apply pending display
-    // transactions.
-    mMockComposer->refreshDisplay(EXTERNAL_DISPLAY);
-    mMockComposer->runVSyncAndWait();
+    Error getDisplayAttributeNoMock(Display display, Config config,
+                                    V2_4::IComposerClient::Attribute attribute, int32_t* outValue) {
+        mFakeComposerClient->setMockHal(nullptr);
+        auto ret =
+                mFakeComposerClient->getDisplayAttribute_2_4(display, config, attribute, outValue);
+        mFakeComposerClient->setMockHal(mMockComposer.get());
+        return ret;
+    }
 
-    // Extra vsync and wait to avoid a 10% flake due to a race.
-    mMockComposer->runVSyncAndWait();
-}
+    void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs,
+                                   Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) {
+        std::vector<Config> configIds;
+        for (int i = 0; i < testConfigs.size(); i++) {
+            configIds.push_back(testConfigs[i].id);
 
-bool DisplayTest::waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
-    int waitCount = 20;
-    while (waitCount--) {
-        while (!mReceivedDisplayEvents.empty()) {
-            auto event = mReceivedDisplayEvents.front();
-            mReceivedDisplayEvents.pop_front();
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::WIDTH, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].w), Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::HEIGHT, _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].h), Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::VSYNC_PERIOD,
+                                                _))
+                    .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].vsyncPeriod),
+                                          Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::CONFIG_GROUP,
+                                                _))
+                    .WillRepeatedly(
+                            DoAll(SetArgPointee<3>(testConfigs[i].group), Return(Error::NONE)));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_X, _))
+                    .WillRepeatedly(Return(Error::UNSUPPORTED));
+            EXPECT_CALL(*mMockComposer,
+                        getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_Y, _))
+                    .WillRepeatedly(Return(Error::UNSUPPORTED));
+        }
 
-            ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
-                     "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
-                     ", connected %d\t",
-                     event.header.displayId, event.hotplug.connected);
+        EXPECT_CALL(*mMockComposer, getDisplayConfigs(display, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(hidl_vec<Config>(configIds)),
+                                      Return(V2_1::Error::NONE)));
 
-            if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
-                event.header.displayId == displayId && event.hotplug.connected == connected) {
-                return true;
+        EXPECT_CALL(*mMockComposer, getActiveConfig(display, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(activeConfig), Return(V2_1::Error::NONE)));
+
+        EXPECT_CALL(*mMockComposer, getDisplayVsyncPeriod(display, _))
+                .WillRepeatedly(
+                        DoAll(SetArgPointee<1>(defaultVsyncPeriod), Return(V2_4::Error::NONE)));
+    }
+
+    void SetUp() override {
+        mMockComposer = std::make_unique<MockComposerHal>();
+        mFakeComposerClient = new FakeComposerClient();
+        mFakeComposerClient->setMockHal(mMockComposer.get());
+
+        sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(mFakeComposerClient);
+        mFakeService = new FakeComposerService(client);
+        ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
+
+        android::hardware::ProcessState::self()->startThreadPool();
+        android::ProcessState::self()->startThreadPool();
+
+        setExpectationsForConfigs(PRIMARY_DISPLAY,
+                                  {{
+                                          .id = 1,
+                                          .w = 1920,
+                                          .h = 1024,
+                                          .vsyncPeriod = 16'666'666,
+                                          .group = 0,
+                                  }},
+                                  1, 16'666'666);
+
+        startSurfaceFlinger();
+
+        // Fake composer wants to enable VSync injection
+        mFakeComposerClient->onSurfaceFlingerStart();
+
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
+                                                 ISurfaceComposer::eConfigChangedDispatch));
+        mLooper = new Looper(false);
+        mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
+    }
+
+    void TearDown() override {
+        mLooper = nullptr;
+        mReceiver = nullptr;
+
+        mComposerClient->dispose();
+        mComposerClient = nullptr;
+
+        // Fake composer needs to release SurfaceComposerClient before the stop.
+        mFakeComposerClient->onSurfaceFlingerStop();
+        stopSurfaceFlinger();
+
+        mFakeComposerClient->setMockHal(nullptr);
+
+        mFakeService = nullptr;
+        // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
+        // management.
+        mMockComposer = nullptr;
+    }
+
+    void waitForDisplayTransaction() {
+        // Both a refresh and a vsync event are needed to apply pending display
+        // transactions.
+        mFakeComposerClient->refreshDisplay(EXTERNAL_DISPLAY);
+        mFakeComposerClient->runVSyncAndWait();
+
+        // Extra vsync and wait to avoid a 10% flake due to a race.
+        mFakeComposerClient->runVSyncAndWait();
+    }
+
+    bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
+        int waitCount = 20;
+        while (waitCount--) {
+            while (!mReceivedDisplayEvents.empty()) {
+                auto event = mReceivedDisplayEvents.front();
+                mReceivedDisplayEvents.pop_front();
+
+                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
+                         "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+                         ", connected %d\t",
+                         event.header.displayId, event.hotplug.connected);
+
+                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
+                    event.header.displayId == displayId && event.hotplug.connected == connected) {
+                    return true;
+                }
+            }
+
+            mLooper->pollOnce(1);
+        }
+        return false;
+    }
+
+    bool waitForConfigChangedEvent(PhysicalDisplayId displayId, int32_t configId) {
+        int waitCount = 20;
+        while (waitCount--) {
+            while (!mReceivedDisplayEvents.empty()) {
+                auto event = mReceivedDisplayEvents.front();
+                mReceivedDisplayEvents.pop_front();
+
+                ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED,
+                         "event config: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+                         ", configId %d\t",
+                         event.header.displayId, event.config.configId);
+
+                if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
+                    event.header.displayId == displayId && event.config.configId == configId) {
+                    return true;
+                }
+            }
+
+            mLooper->pollOnce(1);
+        }
+        return false;
+    }
+
+    void Test_HotplugOneConfig() {
+        ALOGD("DisplayTest::Test_Hotplug_oneConfig");
+
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 1,
+                                    .w = 200,
+                                    .h = 400,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0}},
+                                  1, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+        {
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+            EXPECT_FALSE(display == nullptr);
+
+            DisplayConfig config;
+            EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+            const ui::Size& resolution = config.resolution;
+            EXPECT_EQ(ui::Size(200, 400), resolution);
+            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
             }
         }
 
-        mLooper->pollOnce(1);
-    }
-
-    return false;
-}
-
-TEST_F(DisplayTest, Hotplug) {
-    ALOGD("DisplayTest::Hotplug");
-
-    EXPECT_CALL(*mMockComposer, getDisplayType(EXTERNAL_DISPLAY, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
-                                  Return(Error::NONE)));
-    // The attribute queries will get done twice. This is for defaults
-    EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
-            .Times(2 * 3)
-            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-    // ... and then special handling for dimensions. Specifying these
-    // rules later means that gmock will try them first, i.e.,
-    // ordering of width/height vs. the default implementation for
-    // other queries is significant.
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
-
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
-
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
-
-    waitForDisplayTransaction();
-
-    EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-    {
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
-        ASSERT_FALSE(display == nullptr);
-
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        ASSERT_EQ(400u, info.w);
-        ASSERT_EQ(200u, info.h);
-
-        auto surfaceControl =
-                mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
-                                               PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(surfaceControl != nullptr);
-        ASSERT_TRUE(surfaceControl->isValid());
-        fillSurfaceRGBA8(surfaceControl, BLUE);
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
 
         {
-            TransactionScope ts(*mMockComposer);
-            ts.setDisplayLayerStack(display, 0);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+            EXPECT_TRUE(display == nullptr);
 
-            ts.setLayer(surfaceControl, INT32_MAX - 2)
-                .show(surfaceControl);
+            DisplayConfig config;
+            EXPECT_NE(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
         }
     }
 
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+    void Test_HotplugTwoSeparateConfigs() {
+        ALOGD("DisplayTest::Test_HotplugTwoSeparateConfigs");
 
-    mMockComposer->clearFrames();
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 1,
+                                    .w = 200,
+                                    .h = 400,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0},
+                                   {.id = 2,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 1}},
+                                  1, 16'666'666);
 
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
 
-    waitForDisplayTransaction();
-
-    EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
-    EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
-    {
         const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
-        ASSERT_FALSE(display == nullptr);
+        EXPECT_FALSE(display == nullptr);
 
-        DisplayInfo info;
-        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-        ASSERT_EQ(400u, info.w);
-        ASSERT_EQ(200u, info.h);
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(200, 400), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
 
-        auto surfaceControl =
-                mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
-                                               PIXEL_FORMAT_RGBA_8888, 0);
-        ASSERT_TRUE(surfaceControl != nullptr);
-        ASSERT_TRUE(surfaceControl->isValid());
-        fillSurfaceRGBA8(surfaceControl, BLUE);
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        Vector<DisplayConfig> configs;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+        EXPECT_EQ(configs.size(), 2);
+
+        // change active config
+
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 2))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 800) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+    }
+
+    void Test_HotplugTwoConfigsSameGroup() {
+        ALOGD("DisplayTest::Test_HotplugTwoConfigsSameGroup");
+
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 2,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 31},
+                                   {.id = 3,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 31}},
+                                  2, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+        EXPECT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        Vector<DisplayConfig> configs;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+        EXPECT_EQ(configs.size(), 2);
+
+        // change active config
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.refreshRate == 1e9f / 11'111'111) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+    }
+
+    void Test_HotplugThreeConfigsMixedGroups() {
+        ALOGD("DisplayTest::Test_HotplugThreeConfigsMixedGroups");
+
+        setExpectationsForConfigs(EXTERNAL_DISPLAY,
+                                  {{.id = 2,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0},
+                                   {.id = 3,
+                                    .w = 800,
+                                    .h = 1600,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 0},
+                                   {.id = 4,
+                                    .w = 1600,
+                                    .h = 3200,
+                                    .vsyncPeriod = 8'333'333,
+                                    .group = 1},
+                                   {.id = 5,
+                                    .w = 1600,
+                                    .h = 3200,
+                                    .vsyncPeriod = 11'111'111,
+                                    .group = 1}},
+                                  2, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+        waitForDisplayTransaction();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+        EXPECT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        Vector<DisplayConfig> configs;
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+        EXPECT_EQ(configs.size(), 4);
+
+        // change active config to 800x1600@90Hz
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              configs[i].refreshRate,
+                                                                              configs[i].refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(800, 1600), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        // change active config to 1600x3200@120Hz
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 4))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.refreshRate == 1e9f / 8'333'333) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+        EXPECT_EQ(1e9f / 8'333'333, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        // change active config to 1600x3200@90Hz
+        if (mIs2_4Client) {
+            EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
+                    .WillOnce(Return(V2_4::Error::NONE));
+        } else {
+            EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 5))
+                    .WillOnce(Return(V2_1::Error::NONE));
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            const auto& config = configs[i];
+            if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) {
+                EXPECT_EQ(NO_ERROR,
+                          SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i,
+                                                                              config.refreshRate,
+                                                                              config.refreshRate));
+                waitForDisplayTransaction();
+                EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+                break;
+            }
+        }
+
+        EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+        EXPECT_EQ(ui::Size(1600, 3200), config.resolution);
+        EXPECT_EQ(1e9f / 11'111'111, config.refreshRate);
+
+        mFakeComposerClient->clearFrames();
+        {
+            const ui::Size& resolution = config.resolution;
+            auto surfaceControl =
+                    mComposerClient->createSurface(String8("Display Test Surface Foo"),
+                                                   resolution.getWidth(), resolution.getHeight(),
+                                                   PIXEL_FORMAT_RGBA_8888, 0);
+            EXPECT_TRUE(surfaceControl != nullptr);
+            EXPECT_TRUE(surfaceControl->isValid());
+            fillSurfaceRGBA8(surfaceControl, BLUE);
+
+            {
+                TransactionScope ts(*mFakeComposerClient);
+                ts.setDisplayLayerStack(display, 0);
+
+                ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+            }
+        }
+
+        mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+        waitForDisplayTransaction();
+        mFakeComposerClient->clearFrames();
+        EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+    }
+
+    void Test_HotplugPrimaryDisplay() {
+        ALOGD("DisplayTest::HotplugPrimaryDisplay");
+
+        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::DISCONNECTED);
+
+        waitForDisplayTransaction();
+
+        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
+        {
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+            EXPECT_TRUE(display == nullptr);
+
+            DisplayConfig config;
+            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+            EXPECT_NE(NO_ERROR, result);
+        }
+
+        mFakeComposerClient->clearFrames();
+
+        setExpectationsForConfigs(PRIMARY_DISPLAY,
+                                  {{.id = 1,
+                                    .w = 400,
+                                    .h = 200,
+                                    .vsyncPeriod = 16'666'666,
+                                    .group = 0}},
+                                  1, 16'666'666);
+
+        mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
+                                            V2_1::IComposerCallback::Connection::CONNECTED);
+
+        waitForDisplayTransaction();
+
+        EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
 
         {
-            TransactionScope ts(*mMockComposer);
-            ts.setDisplayLayerStack(display, 0);
+            const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+            EXPECT_FALSE(display == nullptr);
 
-            ts.setLayer(surfaceControl, INT32_MAX - 2)
-                .show(surfaceControl);
+            DisplayConfig config;
+            auto result = SurfaceComposerClient::getActiveDisplayConfig(display, &config);
+            EXPECT_EQ(NO_ERROR, result);
+            ASSERT_EQ(ui::Size(400, 200), config.resolution);
+            EXPECT_EQ(1e9f / 16'666'666, config.refreshRate);
         }
     }
-    mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+
+    sp<V2_1::IComposer> mFakeService;
+    sp<SurfaceComposerClient> mComposerClient;
+
+    std::unique_ptr<MockComposerHal> mMockComposer;
+    FakeComposerClient* mFakeComposerClient;
+
+    std::unique_ptr<DisplayEventReceiver> mReceiver;
+    sp<Looper> mLooper;
+    std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
+
+    static constexpr bool mIs2_4Client =
+            std::is_same<FakeComposerService, FakeComposerService_2_4>::value;
+};
+
+using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
+
+TEST_F(DisplayTest_2_1, HotplugOneConfig) {
+    Test_HotplugOneConfig();
 }
 
-TEST_F(DisplayTest, HotplugPrimaryDisplay) {
-    ALOGD("DisplayTest::HotplugPrimaryDisplay");
+TEST_F(DisplayTest_2_1, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
 
-    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+TEST_F(DisplayTest_2_1, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
 
-    waitForDisplayTransaction();
+TEST_F(DisplayTest_2_1, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
 
-    EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
+TEST_F(DisplayTest_2_1, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
+}
 
-    {
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
-        EXPECT_FALSE(display == nullptr);
+using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>;
 
-        DisplayInfo info;
-        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
-        EXPECT_NE(NO_ERROR, result);
-    }
+TEST_F(DisplayTest_2_2, HotplugOneConfig) {
+    Test_HotplugOneConfig();
+}
 
-    mMockComposer->clearFrames();
+TEST_F(DisplayTest_2_2, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
 
-    EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
-                                  Return(Error::NONE)));
-    // The attribute queries will get done twice. This is for defaults
-    EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
-            .Times(2 * 3)
-            .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-    // ... and then special handling for dimensions. Specifying these
-    // rules later means that gmock will try them first, i.e.,
-    // ordering of width/height vs. the default implementation for
-    // other queries is significant.
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+TEST_F(DisplayTest_2_2, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
 
-    EXPECT_CALL(*mMockComposer,
-                getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
-            .Times(2)
-            .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+TEST_F(DisplayTest_2_2, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
 
-    mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+TEST_F(DisplayTest_2_2, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
+}
 
-    waitForDisplayTransaction();
+using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>;
 
-    EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
+TEST_F(DisplayTest_2_3, HotplugOneConfig) {
+    Test_HotplugOneConfig();
+}
 
-    {
-        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
-        EXPECT_FALSE(display == nullptr);
+TEST_F(DisplayTest_2_3, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
 
-        DisplayInfo info;
-        auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
-        EXPECT_EQ(NO_ERROR, result);
-        ASSERT_EQ(400u, info.w);
-        ASSERT_EQ(200u, info.h);
-    }
+TEST_F(DisplayTest_2_3, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
+
+TEST_F(DisplayTest_2_3, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
+
+TEST_F(DisplayTest_2_3, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
+}
+
+using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>;
+
+TEST_F(DisplayTest_2_4, HotplugOneConfig) {
+    Test_HotplugOneConfig();
+}
+
+TEST_F(DisplayTest_2_4, HotplugTwoSeparateConfigs) {
+    Test_HotplugTwoSeparateConfigs();
+}
+
+TEST_F(DisplayTest_2_4, HotplugTwoConfigsSameGroup) {
+    Test_HotplugTwoConfigsSameGroup();
+}
+
+TEST_F(DisplayTest_2_4, HotplugThreeConfigsMixedGroups) {
+    Test_HotplugThreeConfigsMixedGroups();
+}
+
+TEST_F(DisplayTest_2_4, HotplugPrimaryOneConfig) {
+    Test_HotplugPrimaryDisplay();
 }
 
 ////////////////////////////////////////////////
 
+template <typename FakeComposerService>
 class TransactionTest : public ::testing::Test {
 protected:
     // Layer array indexing constants.
     constexpr static int BG_LAYER = 0;
     constexpr static int FG_LAYER = 1;
 
-    static void SetUpTestCase();
-    static void TearDownTestCase();
+    static void SetUpTestCase() {
+        // TODO: See TODO comment at DisplayTest::SetUp for background on
+        // the lifetime of the FakeComposerClient.
+        sFakeComposer = new FakeComposerClient;
+        sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(sFakeComposer);
+        sp<V2_1::IComposer> fakeService = new FakeComposerService(client);
+        (void)fakeService->registerAsService("mock");
 
-    void SetUp() override;
-    void TearDown() override;
+        android::hardware::ProcessState::self()->startThreadPool();
+        android::ProcessState::self()->startThreadPool();
+
+        startSurfaceFlinger();
+
+        // Fake composer wants to enable VSync injection
+        sFakeComposer->onSurfaceFlingerStart();
+    }
+
+    static void TearDownTestCase() {
+        // Fake composer needs to release SurfaceComposerClient before the stop.
+        sFakeComposer->onSurfaceFlingerStop();
+        stopSurfaceFlinger();
+        // TODO: This is deleted when the ComposerClient calls
+        // removeClient. Devise better lifetime control.
+        sFakeComposer = nullptr;
+    }
+
+    void SetUp() override {
+        ALOGI("TransactionTest::SetUp");
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        ALOGI("TransactionTest::SetUp - display");
+        const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+        ASSERT_FALSE(display == nullptr);
+
+        DisplayConfig config;
+        ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config));
+
+        const ui::Size& resolution = config.resolution;
+        mDisplayWidth = resolution.getWidth();
+        mDisplayHeight = resolution.getHeight();
+
+        // Background surface
+        mBGSurfaceControl =
+                mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
+                                               mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mBGSurfaceControl != nullptr);
+        ASSERT_TRUE(mBGSurfaceControl->isValid());
+        fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
+
+        // Foreground surface
+        mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
+                                                           PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(mFGSurfaceControl != nullptr);
+        ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+        fillSurfaceRGBA8(mFGSurfaceControl, RED);
+
+        Transaction t;
+        t.setDisplayLayerStack(display, 0);
+
+        t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
+        t.show(mBGSurfaceControl);
+
+        t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
+        t.setPosition(mFGSurfaceControl, 64, 64);
+        t.show(mFGSurfaceControl);
+
+        // Synchronous transaction will stop this thread, so we set up a
+        // delayed, off-thread vsync request before closing the
+        // transaction. In the test code this is usually done with
+        // TransactionScope. Leaving here in the 'vanilla' form for
+        // reference.
+        ASSERT_EQ(0, sFakeComposer->getFrameCount());
+        sFakeComposer->runVSyncAfter(1ms);
+        t.apply();
+        sFakeComposer->waitUntilFrame(1);
+
+        // Reference data. This is what the HWC should see.
+        static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
+        mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
+        mBaseFrame[BG_LAYER].mSwapCount = 1;
+        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+        mBaseFrame[FG_LAYER].mSwapCount = 1;
+
+        auto frame = sFakeComposer->getFrameRects(0);
+        ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+    }
+
+    void TearDown() override {
+        ALOGD("TransactionTest::TearDown");
+
+        mComposerClient->dispose();
+        mBGSurfaceControl = 0;
+        mFGSurfaceControl = 0;
+        mComposerClient = 0;
+
+        sFakeComposer->runVSyncAndWait();
+        mBaseFrame.clear();
+        sFakeComposer->clearFrames();
+        ASSERT_EQ(0, sFakeComposer->getFrameCount());
+
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        std::vector<LayerDebugInfo> layers;
+        status_t result = sf->getLayerDebugInfo(&layers);
+        if (result != NO_ERROR) {
+            ALOGE("Failed to get layers %s %d", strerror(-result), result);
+        } else {
+            // If this fails, the test being torn down leaked layers.
+            EXPECT_EQ(0u, layers.size());
+            if (layers.size() > 0) {
+                for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
+                    std::cout << to_string(*layer).c_str();
+                }
+                // To ensure the next test has clean slate, will run the class
+                // tear down and setup here.
+                TearDownTestCase();
+                SetUpTestCase();
+            }
+        }
+        ALOGD("TransactionTest::TearDown - complete");
+    }
+
+    void Test_LayerMove() {
+        ALOGD("TransactionTest::LayerMove");
+
+        // The scope opens and closes a global transaction and, at the
+        // same time, makes sure the SurfaceFlinger progresses one frame
+        // after the transaction closes. The results of the transaction
+        // should be available in the latest frame stored by the fake
+        // composer.
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setPosition(mFGSurfaceControl, 128, 128);
+            // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
+            // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
+            //
+            // sFakeComposer->runVSyncAndWait();
+        }
+
+        fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+        sFakeComposer->runVSyncAndWait();
+
+        ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
+                                                      // there's no extra frames.
+
+        // NOTE: Frame 0 is produced in the SetUp.
+        auto frame1Ref = mBaseFrame;
+        frame1Ref[FG_LAYER].mDisplayFrame =
+                hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
+        EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+        auto frame2Ref = frame1Ref;
+        frame2Ref[FG_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+    }
+
+    void Test_LayerResize() {
+        ALOGD("TransactionTest::LayerResize");
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setSize(mFGSurfaceControl, 128, 128);
+        }
+
+        fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+        sFakeComposer->runVSyncAndWait();
+
+        ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
+                                                      // there's no extra frames.
+
+        auto frame1Ref = mBaseFrame;
+        // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size
+        // posted.
+        EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+        auto frame2Ref = frame1Ref;
+        frame2Ref[FG_LAYER].mSwapCount++;
+        frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
+        frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
+        EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+    }
+
+    void Test_LayerCrop() {
+        // TODO: Add scaling to confirm that crop happens in buffer space?
+        {
+            TransactionScope ts(*sFakeComposer);
+            Rect cropRect(16, 16, 32, 32);
+            ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+        }
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+        auto referenceFrame = mBaseFrame;
+        referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
+        referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetLayer() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
+        }
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+        // The layers will switch order, but both are rendered because the background layer is
+        // transparent (RGBA8888).
+        std::vector<RenderState> referenceFrame(2);
+        referenceFrame[0] = mBaseFrame[FG_LAYER];
+        referenceFrame[1] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetLayerOpaque() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
+            ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
+                        layer_state_t::eLayerOpaque);
+        }
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+        // The former foreground layer is now covered with opaque layer - it should have disappeared
+        std::vector<RenderState> referenceFrame(1);
+        referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_SetLayerStack() {
+        ALOGD("TransactionTest::SetLayerStack");
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayerStack(mFGSurfaceControl, 1);
+        }
+
+        // Foreground layer should have disappeared.
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        std::vector<RenderState> refFrame(1);
+        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerShowHide() {
+        ALOGD("TransactionTest::LayerShowHide");
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.hide(mFGSurfaceControl);
+        }
+
+        // Foreground layer should have disappeared.
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        std::vector<RenderState> refFrame(1);
+        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.show(mFGSurfaceControl);
+        }
+
+        // Foreground layer should be back
+        ASSERT_EQ(3, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetAlpha() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setAlpha(mFGSurfaceControl, 0.75f);
+        }
+
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        auto referenceFrame = mBaseFrame;
+        referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetFlags() {
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
+                        layer_state_t::eLayerHidden);
+        }
+
+        // Foreground layer should have disappeared.
+        ASSERT_EQ(2, sFakeComposer->getFrameCount());
+        std::vector<RenderState> refFrame(1);
+        refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+        EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerSetMatrix() {
+        struct matrixTestData {
+            float matrix[4];
+            hwc_transform_t expectedTransform;
+            hwc_rect_t expectedDisplayFrame;
+        };
+
+        // The matrix operates on the display frame and is applied before
+        // the position is added. So, the foreground layer rect is (0, 0,
+        // 64, 64) is first transformed, potentially yielding negative
+        // coordinates and then the position (64, 64) is added yielding
+        // the final on-screen rectangles given.
+
+        const matrixTestData MATRIX_TESTS[7] = // clang-format off
+                {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
+                 {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
+                 {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
+                 {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
+                 {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
+                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
+                 {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
+        // clang-format on
+        constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
+
+        for (int i = 0; i < TEST_COUNT; i++) {
+            // TODO: How to leverage the HWC2 stringifiers?
+            const matrixTestData& xform = MATRIX_TESTS[i];
+            SCOPED_TRACE(i);
+            {
+                TransactionScope ts(*sFakeComposer);
+                ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1], xform.matrix[2],
+                             xform.matrix[3]);
+            }
+
+            auto referenceFrame = mBaseFrame;
+            referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
+            referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
+
+            EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+        }
+    }
+
+    void Test_DeferredTransaction() {
+        // Synchronization surface
+        constexpr static int SYNC_LAYER = 2;
+        auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
+                                                                 PIXEL_FORMAT_RGBA_8888, 0);
+        ASSERT_TRUE(syncSurfaceControl != nullptr);
+        ASSERT_TRUE(syncSurfaceControl->isValid());
+
+        fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
+            ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
+            ts.show(syncSurfaceControl);
+        }
+        auto referenceFrame = mBaseFrame;
+        referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
+                                                mDisplayWidth - 1, mDisplayHeight - 1));
+        referenceFrame[SYNC_LAYER].mSwapCount = 1;
+        EXPECT_EQ(2, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // set up two deferred transactions on different frames - these should not yield composited
+        // frames
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setAlpha(mFGSurfaceControl, 0.75);
+            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_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+                                            syncSurfaceControl->getSurface()->getNextFrameNumber() +
+                                                    1);
+        }
+        EXPECT_EQ(4, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // should trigger the first deferred transaction, but not the second one
+        fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+        sFakeComposer->runVSyncAndWait();
+        EXPECT_EQ(5, sFakeComposer->getFrameCount());
+
+        referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+        referenceFrame[SYNC_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // should show up immediately since it's not deferred
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setAlpha(mFGSurfaceControl, 1.0);
+        }
+        referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
+        EXPECT_EQ(6, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // trigger the second deferred transaction
+        fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+        sFakeComposer->runVSyncAndWait();
+        // TODO: Compute from layer size?
+        referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
+        referenceFrame[SYNC_LAYER].mSwapCount++;
+        EXPECT_EQ(7, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_SetRelativeLayer() {
+        constexpr int RELATIVE_LAYER = 2;
+        auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64,
+                                                                     64, PIXEL_FORMAT_RGBA_8888, 0);
+        fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
+
+        // Now we stack the surface above the foreground surface and make sure it is visible.
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setPosition(relativeSurfaceControl, 64, 64);
+            ts.show(relativeSurfaceControl);
+            ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
+        }
+        auto referenceFrame = mBaseFrame;
+        // NOTE: All three layers will be visible as the surfaces are
+        // transparent because of the RGBA format.
+        referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+        referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
+        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+        // A call to setLayer will override a call to setRelativeLayer
+        {
+            TransactionScope ts(*sFakeComposer);
+            ts.setLayer(relativeSurfaceControl, 0);
+        }
+
+        // Previous top layer will now appear at the bottom.
+        auto referenceFrame2 = mBaseFrame;
+        referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
+        EXPECT_EQ(3, sFakeComposer->getFrameCount());
+        EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+    }
 
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mBGSurfaceControl;
@@ -442,944 +1389,609 @@
     uint32_t mDisplayWidth;
     uint32_t mDisplayHeight;
 
-    static FakeComposerClient* sFakeComposer;
+    static inline FakeComposerClient* sFakeComposer;
 };
 
-FakeComposerClient* TransactionTest::sFakeComposer;
+using TransactionTest_2_1 = TransactionTest<FakeComposerService_2_1>;
 
-void TransactionTest::SetUpTestCase() {
-    // TODO: See TODO comment at DisplayTest::SetUp for background on
-    // the lifetime of the FakeComposerClient.
-    sFakeComposer = new FakeComposerClient;
-    sp<ComposerClient> client = new ComposerClient(sFakeComposer);
-    sp<IComposer> fakeService = new FakeComposerService(client);
-    (void)fakeService->registerAsService("mock");
-
-    android::hardware::ProcessState::self()->startThreadPool();
-    android::ProcessState::self()->startThreadPool();
-
-    startSurfaceFlinger();
-
-    // Fake composer wants to enable VSync injection
-    sFakeComposer->onSurfaceFlingerStart();
+TEST_F(TransactionTest_2_1, DISABLED_LayerMove) {
+    Test_LayerMove();
 }
 
-void TransactionTest::TearDownTestCase() {
-    // Fake composer needs to release SurfaceComposerClient before the stop.
-    sFakeComposer->onSurfaceFlingerStop();
-    stopSurfaceFlinger();
-    // TODO: This is deleted when the ComposerClient calls
-    // removeClient. Devise better lifetime control.
-    sFakeComposer = nullptr;
+TEST_F(TransactionTest_2_1, DISABLED_LayerResize) {
+    Test_LayerResize();
 }
 
-void TransactionTest::SetUp() {
-    ALOGI("TransactionTest::SetUp");
-    mComposerClient = new SurfaceComposerClient;
-    ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
-    ALOGI("TransactionTest::SetUp - display");
-    const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
-    ASSERT_FALSE(display == nullptr);
-
-    DisplayInfo info;
-    ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
-    mDisplayWidth = info.w;
-    mDisplayHeight = info.h;
-
-    // Background surface
-    mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
-                                                       mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mBGSurfaceControl != nullptr);
-    ASSERT_TRUE(mBGSurfaceControl->isValid());
-    fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
-
-    // Foreground surface
-    mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
-                                                       PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(mFGSurfaceControl != nullptr);
-    ASSERT_TRUE(mFGSurfaceControl->isValid());
-
-    fillSurfaceRGBA8(mFGSurfaceControl, RED);
-
-    Transaction t;
-    t.setDisplayLayerStack(display, 0);
-
-    t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
-    t.show(mBGSurfaceControl);
-
-    t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
-    t.setPosition(mFGSurfaceControl, 64, 64);
-    t.show(mFGSurfaceControl);
-
-    // Synchronous transaction will stop this thread, so we set up a
-    // delayed, off-thread vsync request before closing the
-    // transaction. In the test code this is usually done with
-    // TransactionScope. Leaving here in the 'vanilla' form for
-    // reference.
-    ASSERT_EQ(0, sFakeComposer->getFrameCount());
-    sFakeComposer->runVSyncAfter(1ms);
-    t.apply();
-    sFakeComposer->waitUntilFrame(1);
-
-    // Reference data. This is what the HWC should see.
-    static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
-    mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
-    mBaseFrame[BG_LAYER].mSwapCount = 1;
-    mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-    mBaseFrame[FG_LAYER].mSwapCount = 1;
-
-    auto frame = sFakeComposer->getFrameRects(0);
-    ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
+    Test_LayerCrop();
 }
 
-void TransactionTest::TearDown() {
-    ALOGD("TransactionTest::TearDown");
-
-    mComposerClient->dispose();
-    mBGSurfaceControl = 0;
-    mFGSurfaceControl = 0;
-    mComposerClient = 0;
-
-    sFakeComposer->runVSyncAndWait();
-    mBaseFrame.clear();
-    sFakeComposer->clearFrames();
-    ASSERT_EQ(0, sFakeComposer->getFrameCount());
-
-    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
-    std::vector<LayerDebugInfo> layers;
-    status_t result = sf->getLayerDebugInfo(&layers);
-    if (result != NO_ERROR) {
-        ALOGE("Failed to get layers %s %d", strerror(-result), result);
-    } else {
-        // If this fails, the test being torn down leaked layers.
-        EXPECT_EQ(0u, layers.size());
-        if (layers.size() > 0) {
-            for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
-                std::cout << to_string(*layer).c_str();
-            }
-            // To ensure the next test has clean slate, will run the class
-            // tear down and setup here.
-            TearDownTestCase();
-            SetUpTestCase();
-        }
-    }
-    ALOGD("TransactionTest::TearDown - complete");
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayer) {
+    Test_LayerSetLayer();
 }
 
-TEST_F(TransactionTest, LayerMove) {
-    ALOGD("TransactionTest::LayerMove");
-
-    // The scope opens and closes a global transaction and, at the
-    // same time, makes sure the SurfaceFlinger progresses one frame
-    // after the transaction closes. The results of the transaction
-    // should be available in the latest frame stored by the fake
-    // composer.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 128, 128);
-        // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
-        // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
-        //
-        // sFakeComposer->runVSyncAndWait();
-    }
-
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
-
-    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
-                                                  // no extra frames.
-
-    // NOTE: Frame 0 is produced in the SetUp.
-    auto frame1Ref = mBaseFrame;
-    frame1Ref[FG_LAYER].mDisplayFrame =
-            hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
-    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
-    auto frame2Ref = frame1Ref;
-    frame2Ref[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayerOpaque) {
+    Test_LayerSetLayerOpaque();
 }
 
-TEST_F(TransactionTest, LayerResize) {
-    ALOGD("TransactionTest::LayerResize");
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-    }
-
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
-
-    ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
-                                                  // no extra frames.
-
-    auto frame1Ref = mBaseFrame;
-    // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
-    EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
-    auto frame2Ref = frame1Ref;
-    frame2Ref[FG_LAYER].mSwapCount++;
-    frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
-    frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
-    EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+TEST_F(TransactionTest_2_1, DISABLED_SetLayerStack) {
+    Test_SetLayerStack();
 }
 
-TEST_F(TransactionTest, LayerCrop) {
-    // TODO: Add scaling to confirm that crop happens in buffer space?
-    {
-        TransactionScope ts(*sFakeComposer);
-        Rect cropRect(16, 16, 32, 32);
-        ts.setCrop_legacy(mFGSurfaceControl, cropRect);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerShowHide) {
+    Test_LayerShowHide();
 }
 
-TEST_F(TransactionTest, LayerSetLayer) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    // The layers will switch order, but both are rendered because the background layer is
-    // transparent (RGBA8888).
-    std::vector<RenderState> referenceFrame(2);
-    referenceFrame[0] = mBaseFrame[FG_LAYER];
-    referenceFrame[1] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetAlpha) {
+    Test_LayerSetAlpha();
 }
 
-TEST_F(TransactionTest, LayerSetLayerOpaque) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
-        ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
-                layer_state_t::eLayerOpaque);
-    }
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
-    // The former foreground layer is now covered with opaque layer - it should have disappeared
-    std::vector<RenderState> referenceFrame(1);
-    referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetFlags) {
+    Test_LayerSetFlags();
 }
 
-TEST_F(TransactionTest, SetLayerStack) {
-    ALOGD("TransactionTest::SetLayerStack");
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayerStack(mFGSurfaceControl, 1);
-    }
-
-    // Foreground layer should have disappeared.
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    std::vector<RenderState> refFrame(1);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetMatrix) {
+    Test_LayerSetMatrix();
 }
 
-TEST_F(TransactionTest, LayerShowHide) {
-    ALOGD("TransactionTest::LayerShowHide");
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.hide(mFGSurfaceControl);
-    }
-
-    // Foreground layer should have disappeared.
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    std::vector<RenderState> refFrame(1);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mFGSurfaceControl);
-    }
-
-    // Foreground layer should be back
-    ASSERT_EQ(3, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_DeferredTransaction) {
+    Test_DeferredTransaction();
 }
 
-TEST_F(TransactionTest, LayerSetAlpha) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 0.75f);
-    }
-
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
+    Test_SetRelativeLayer();
 }
 
-TEST_F(TransactionTest, LayerSetFlags) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
-                layer_state_t::eLayerHidden);
-    }
+template <typename FakeComposerService>
+class ChildLayerTest : public TransactionTest<FakeComposerService> {
+    using Base = TransactionTest<FakeComposerService>;
 
-    // Foreground layer should have disappeared.
-    ASSERT_EQ(2, sFakeComposer->getFrameCount());
-    std::vector<RenderState> refFrame(1);
-    refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-    EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, LayerSetMatrix) {
-    struct matrixTestData {
-        float matrix[4];
-        hwc_transform_t expectedTransform;
-        hwc_rect_t expectedDisplayFrame;
-    };
-
-    // The matrix operates on the display frame and is applied before
-    // the position is added. So, the foreground layer rect is (0, 0,
-    // 64, 64) is first transformed, potentially yielding negative
-    // coordinates and then the position (64, 64) is added yielding
-    // the final on-screen rectangles given.
-
-    const matrixTestData MATRIX_TESTS[7] = // clang-format off
-            {{{-1.f, 0.f, 0.f, 1.f},    HWC_TRANSFORM_FLIP_H,           {0, 64, 64, 128}},
-             {{1.f, 0.f, 0.f, -1.f},    HWC_TRANSFORM_FLIP_V,           {64, 0, 128, 64}},
-             {{0.f, 1.f, -1.f, 0.f},    HWC_TRANSFORM_ROT_90,           {0, 64, 64, 128}},
-             {{-1.f, 0.f, 0.f, -1.f},   HWC_TRANSFORM_ROT_180,          {0, 0, 64, 64}},
-             {{0.f, -1.f, 1.f, 0.f},    HWC_TRANSFORM_ROT_270,          {64, 0, 128, 64}},
-             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_H_ROT_90,    {64, 64, 128, 128}},
-             {{0.f, 1.f, 1.f, 0.f},     HWC_TRANSFORM_FLIP_V_ROT_90,    {64, 64, 128, 128}}};
-    // clang-format on
-    constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
-
-    for (int i = 0; i < TEST_COUNT; i++) {
-        // TODO: How to leverage the HWC2 stringifiers?
-        const matrixTestData& xform = MATRIX_TESTS[i];
-        SCOPED_TRACE(i);
-        {
-            TransactionScope ts(*sFakeComposer);
-            ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1],
-                    xform.matrix[2], xform.matrix[3]);
-        }
-
-        auto referenceFrame = mBaseFrame;
-        referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
-        referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
-
-        EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-    }
-}
-
-#if 0
-TEST_F(TransactionTest, LayerSetMatrix2) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        // TODO: PLEASE SPEC THE FUNCTION!
-        ts.setMatrix(mFGSurfaceControl, 0.11f, 0.123f,
-                -2.33f, 0.22f);
-    }
-    auto referenceFrame = mBaseFrame;
-    // TODO: Is this correct for sure?
-    //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
-
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-#endif
-
-TEST_F(TransactionTest, DeferredTransaction) {
-    // Synchronization surface
-    constexpr static int SYNC_LAYER = 2;
-    auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
-                                                             PIXEL_FORMAT_RGBA_8888, 0);
-    ASSERT_TRUE(syncSurfaceControl != nullptr);
-    ASSERT_TRUE(syncSurfaceControl->isValid());
-
-    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
-        ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
-        ts.show(syncSurfaceControl);
-    }
-    auto referenceFrame = mBaseFrame;
-    referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
-                                            mDisplayWidth - 1, mDisplayHeight - 1));
-    referenceFrame[SYNC_LAYER].mSwapCount = 1;
-    EXPECT_EQ(2, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // set up two deferred transactions on different frames - these should not yield composited
-    // frames
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 0.75);
-        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_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
-                                        syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
-    }
-    EXPECT_EQ(4, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // should trigger the first deferred transaction, but not the second one
-    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-    sFakeComposer->runVSyncAndWait();
-    EXPECT_EQ(5, sFakeComposer->getFrameCount());
-
-    referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
-    referenceFrame[SYNC_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // should show up immediately since it's not deferred
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setAlpha(mFGSurfaceControl, 1.0);
-    }
-    referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
-    EXPECT_EQ(6, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // trigger the second deferred transaction
-    fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-    sFakeComposer->runVSyncAndWait();
-    // TODO: Compute from layer size?
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
-    referenceFrame[SYNC_LAYER].mSwapCount++;
-    EXPECT_EQ(7, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, SetRelativeLayer) {
-    constexpr int RELATIVE_LAYER = 2;
-    auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
-                                                                 PIXEL_FORMAT_RGBA_8888, 0);
-    fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
-
-    // Now we stack the surface above the foreground surface and make sure it is visible.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(relativeSurfaceControl, 64, 64);
-        ts.show(relativeSurfaceControl);
-        ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
-    }
-    auto referenceFrame = mBaseFrame;
-    // NOTE: All three layers will be visible as the surfaces are
-    // transparent because of the RGBA format.
-    referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
-    referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    // A call to setLayer will override a call to setRelativeLayer
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setLayer(relativeSurfaceControl, 0);
-    }
-
-    // Previous top layer will now appear at the bottom.
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
-    EXPECT_EQ(3, sFakeComposer->getFrameCount());
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-}
-
-class ChildLayerTest : public TransactionTest {
 protected:
     constexpr static int CHILD_LAYER = 2;
 
     void SetUp() override {
-        TransactionTest::SetUp();
-        mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                                PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+        Base::SetUp();
+        mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                                      PIXEL_FORMAT_RGBA_8888, 0,
+                                                      Base::mFGSurfaceControl.get());
         fillSurfaceRGBA8(mChild, LIGHT_GRAY);
 
-        sFakeComposer->runVSyncAndWait();
-        mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
-        mBaseFrame[CHILD_LAYER].mSwapCount = 1;
-        ASSERT_EQ(2, sFakeComposer->getFrameCount());
-        ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+        Base::sFakeComposer->runVSyncAndWait();
+        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+        Base::mBaseFrame[CHILD_LAYER].mSwapCount = 1;
+        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
+        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
     }
+
     void TearDown() override {
         mChild = 0;
-        TransactionTest::TearDown();
+        Base::TearDown();
+    }
+
+    void Test_Positioning() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 10, 10);
+            // Move to the same position as in the original setup.
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::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, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
+        referenceFrame2[CHILD_LAYER].mDisplayFrame =
+                hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Cropping() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setCrop_legacy(Base::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
+        // transparent.
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+        referenceFrame[Base::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, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Constraints() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setPosition(mChild, 63, 63);
+        }
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
+        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Scaling() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setMatrix(Base::mFGSurfaceControl, 2.0, 0, 0, 2.0);
+        }
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+        referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerAlpha() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setAlpha(mChild, 0.5);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::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, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
+        }
+
+        auto referenceFrame2 = referenceFrame;
+        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
+        referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_ReparentChildren() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 10, 10);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::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, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl->getHandle());
+        }
+
+        auto referenceFrame2 = referenceFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+        referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_DetachChildrenSameClient() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 10, 10);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::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, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.detachChildren(Base::mFGSurfaceControl);
+        }
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+            ts.hide(mChild);
+        }
+
+        std::vector<RenderState> refFrame(2);
+        refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
+        refFrame[Base::FG_LAYER] = Base::mBaseFrame[Base::FG_LAYER];
+
+        EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_DetachChildrenDifferentClient() {
+        sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+        sp<SurfaceControl> childNewClient =
+                newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+                                                 PIXEL_FORMAT_RGBA_8888, 0,
+                                                 Base::mFGSurfaceControl.get());
+        ASSERT_TRUE(childNewClient != nullptr);
+        ASSERT_TRUE(childNewClient->isValid());
+        fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.hide(mChild);
+            ts.show(childNewClient);
+            ts.setPosition(childNewClient, 10, 10);
+            ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::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, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.detachChildren(Base::mFGSurfaceControl);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setPosition(Base::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.
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_InheritNonTransformScalingFromParent() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setOverrideScalingMode(Base::mFGSurfaceControl,
+                                      NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+            // We cause scaling by 2.
+            ts.setSize(Base::mFGSurfaceControl, 128, 128);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+        referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    // Regression test for b/37673612
+    void Test_ChildrenWithParentBufferTransform() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(mChild);
+            ts.setPosition(mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+        }
+
+        // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+        // the WM specified state size.
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setSize(Base::mFGSurfaceControl, 128, 64);
+        }
+
+        sp<Surface> s = Base::mFGSurfaceControl->getSurface();
+        auto anw = static_cast<ANativeWindow*>(s.get());
+        native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+        native_window_set_buffers_dimensions(anw, 64, 128);
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+        Base::sFakeComposer->runVSyncAndWait();
+
+        // The child should still be in the same place and not have any strange scaling as in
+        // b/37673612.
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
+        referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
+        referenceFrame[Base::FG_LAYER].mSwapCount++;
+        referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_Bug36858924() {
+        // Destroy the child layer
+        mChild.clear();
+
+        // Now recreate it as hidden
+        mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
+                                                      PIXEL_FORMAT_RGBA_8888,
+                                                      ISurfaceComposerClient::eHidden,
+                                                      Base::mFGSurfaceControl.get());
+
+        // Show the child layer in a deferred transaction
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl->getHandle(),
+                                            Base::mFGSurfaceControl->getSurface()
+                                                    ->getNextFrameNumber());
+            ts.show(mChild);
+        }
+
+        // Render the foreground surface a few times
+        //
+        // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the
+        // third frame because SurfaceFlinger would never process the deferred transaction and would
+        // therefore never acquire/release the first buffer
+        ALOGI("Filling 1");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
+        Base::sFakeComposer->runVSyncAndWait();
+        ALOGI("Filling 2");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, BLUE);
+        Base::sFakeComposer->runVSyncAndWait();
+        ALOGI("Filling 3");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+        Base::sFakeComposer->runVSyncAndWait();
+        ALOGI("Filling 4");
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
+        Base::sFakeComposer->runVSyncAndWait();
     }
 
     sp<SurfaceControl> mChild;
 };
 
-TEST_F(ChildLayerTest, Positioning) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 10, 10);
-        // Move to the same position as in the original setup.
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-    }
+using ChildLayerTest_2_1 = ChildLayerTest<FakeComposerService_2_1>;
 
-    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.setPosition(mFGSurfaceControl, 0, 0);
-    }
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
-    referenceFrame2[CHILD_LAYER].mDisplayFrame =
-            hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Positioning) {
+    Test_Positioning();
 }
 
-TEST_F(ChildLayerTest, Cropping) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        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
-    // transparent.
-    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_2_1, DISABLED_Cropping) {
+    Test_Cropping();
 }
 
-TEST_F(ChildLayerTest, Constraints) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-        ts.setPosition(mChild, 63, 63);
-    }
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
-    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Constraints) {
+    Test_Constraints();
 }
 
-TEST_F(ChildLayerTest, Scaling) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-    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};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0);
-    }
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
-    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Scaling) {
+    Test_Scaling();
 }
 
-TEST_F(ChildLayerTest, 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(ChildLayerTest_2_1, DISABLED_LayerAlpha) {
+    Test_LayerAlpha();
 }
 
-TEST_F(ChildLayerTest, ReparentChildren) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 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.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
-    }
-
-    auto referenceFrame2 = referenceFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
-    referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_ReparentChildren) {
+    Test_ReparentChildren();
 }
 
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 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.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_2_1, DISABLED_DetachChildrenSameClient) {
+    Test_DetachChildrenSameClient();
 }
 
-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.
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenDifferentClient) {
+    Test_DetachChildrenDifferentClient();
 }
 
-TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-        // We cause scaling by 2.
-        ts.setSize(mFGSurfaceControl, 128, 128);
-    }
-
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
-    referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_InheritNonTransformScalingFromParent) {
+    Test_InheritNonTransformScalingFromParent();
 }
 
 // Regression test for b/37673612
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.show(mChild);
-        ts.setPosition(mChild, 0, 0);
-        ts.setPosition(mFGSurfaceControl, 0, 0);
-    }
-
-    // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
-    // the WM specified state size.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 64);
-    }
-
-    sp<Surface> s = mFGSurfaceControl->getSurface();
-    auto anw = static_cast<ANativeWindow*>(s.get());
-    native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
-    native_window_set_buffers_dimensions(anw, 64, 128);
-    fillSurfaceRGBA8(mFGSurfaceControl, RED);
-    sFakeComposer->runVSyncAndWait();
-
-    // The child should still be in the same place and not have any strange scaling as in
-    // b/37673612.
-    auto referenceFrame = mBaseFrame;
-    referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
-    referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
-    referenceFrame[FG_LAYER].mSwapCount++;
-    referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
-    EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
+    Test_ChildrenWithParentBufferTransform();
 }
 
-TEST_F(ChildLayerTest, Bug36858924) {
-    // Destroy the child layer
-    mChild.clear();
-
-    // Now recreate it as hidden
-    mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
-                                            PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
-                                            mFGSurfaceControl.get());
-
-    // Show the child layer in a deferred transaction
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
-                                        mFGSurfaceControl->getSurface()->getNextFrameNumber());
-        ts.show(mChild);
-    }
-
-    // Render the foreground surface a few times
-    //
-    // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
-    // frame because SurfaceFlinger would never process the deferred transaction and would therefore
-    // never acquire/release the first buffer
-    ALOGI("Filling 1");
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
-    ALOGI("Filling 2");
-    fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
-    sFakeComposer->runVSyncAndWait();
-    ALOGI("Filling 3");
-    fillSurfaceRGBA8(mFGSurfaceControl, RED);
-    sFakeComposer->runVSyncAndWait();
-    ALOGI("Filling 4");
-    fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
-    sFakeComposer->runVSyncAndWait();
+TEST_F(ChildLayerTest_2_1, DISABLED_Bug36858924) {
+    Test_Bug36858924();
 }
 
-class ChildColorLayerTest : public ChildLayerTest {
+template <typename FakeComposerService>
+class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
+    using Base = ChildLayerTest<FakeComposerService>;
+
 protected:
     void SetUp() override {
-        TransactionTest::SetUp();
-        mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0,
-                                                PIXEL_FORMAT_RGBA_8888,
-                                                ISurfaceComposerClient::eFXSurfaceColor,
-                                                mFGSurfaceControl.get());
+        Base::SetUp();
+        Base::mChild =
+                Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
+                                                     PIXEL_FORMAT_RGBA_8888,
+                                                     ISurfaceComposerClient::eFXSurfaceEffect,
+                                                     Base::mFGSurfaceControl.get());
         {
-            TransactionScope ts(*sFakeComposer);
-            ts.setColor(mChild,
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setColor(Base::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));
+            ts.setCrop_legacy(Base::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()));
+        Base::sFakeComposer->runVSyncAndWait();
+        Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+        Base::mBaseFrame[Base::CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
+        Base::mBaseFrame[Base::CHILD_LAYER].mSwapCount = 0;
+        ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
+        ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerAlpha() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(Base::mChild);
+            ts.setPosition(Base::mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setAlpha(Base::mChild, 0.5);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setAlpha(Base::mFGSurfaceControl, 0.5);
+        }
+
+        auto referenceFrame2 = referenceFrame;
+        referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
+        referenceFrame2[Base::CHILD_LAYER].mPlaneAlpha = 0.25f;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_LayerZeroAlpha() {
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.show(Base::mChild);
+            ts.setPosition(Base::mChild, 0, 0);
+            ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+            ts.setAlpha(Base::mChild, 0.5);
+        }
+
+        auto referenceFrame = Base::mBaseFrame;
+        referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+        referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+        referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
+        EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setAlpha(Base::mFGSurfaceControl, 0.0f);
+        }
+
+        std::vector<RenderState> refFrame(1);
+        refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
+
+        EXPECT_TRUE(framesAreSame(refFrame, Base::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);
-    }
+using ChildColorLayerTest_2_1 = ChildColorLayerTest<FakeComposerService_2_1>;
 
-    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_2_1, DISABLED_LayerAlpha) {
+    Test_LayerAlpha();
 }
 
-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()));
+TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
+    Test_LayerZeroAlpha();
 }
 
-class LatchingTest : public TransactionTest {
+template <typename FakeComposerService>
+class LatchingTest : public TransactionTest<FakeComposerService> {
+    using Base = TransactionTest<FakeComposerService>;
+
 protected:
-    void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
+    void lockAndFillFGBuffer() { fillSurfaceRGBA8(Base::mFGSurfaceControl, RED, false); }
 
     void unlockFGBuffer() {
-        sp<Surface> s = mFGSurfaceControl->getSurface();
+        sp<Surface> s = Base::mFGSurfaceControl->getSurface();
         ASSERT_EQ(NO_ERROR, s->unlockAndPost());
-        sFakeComposer->runVSyncAndWait();
+        Base::sFakeComposer->runVSyncAndWait();
     }
 
     void completeFGResize() {
-        fillSurfaceRGBA8(mFGSurfaceControl, RED);
-        sFakeComposer->runVSyncAndWait();
+        fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+        Base::sFakeComposer->runVSyncAndWait();
     }
     void restoreInitialState() {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 64, 64);
-        ts.setPosition(mFGSurfaceControl, 64, 64);
-        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+        TransactionScope ts(*Base::sFakeComposer);
+        ts.setSize(Base::mFGSurfaceControl, 64, 64);
+        ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+        ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
+    }
+
+    void Test_SurfacePositionLatching() {
+        // By default position can be updated even while
+        // a resize is pending.
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setSize(Base::mFGSurfaceControl, 32, 32);
+            ts.setPosition(Base::mFGSurfaceControl, 100, 100);
+        }
+
+        // The size should not have updated as we have not provided a new buffer.
+        auto referenceFrame1 = Base::mBaseFrame;
+        referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
+        EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
+
+        restoreInitialState();
+
+        completeFGResize();
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
+        referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+        referenceFrame2[Base::FG_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+    }
+
+    void Test_CropLatching() {
+        // Normally the crop applies immediately even while a resize is pending.
+        {
+            TransactionScope ts(*Base::sFakeComposer);
+            ts.setSize(Base::mFGSurfaceControl, 128, 128);
+            ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
+        }
+
+        auto referenceFrame1 = Base::mBaseFrame;
+        referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+        referenceFrame1[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+        EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
+
+        restoreInitialState();
+
+        completeFGResize();
+
+        auto referenceFrame2 = Base::mBaseFrame;
+        referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+        referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+        referenceFrame2[Base::FG_LAYER].mSwapCount++;
+        EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
     }
 };
 
-TEST_F(LatchingTest, SurfacePositionLatching) {
-    // By default position can be updated even while
-    // a resize is pending.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 32, 32);
-        ts.setPosition(mFGSurfaceControl, 100, 100);
-    }
+using LatchingTest_2_1 = LatchingTest<FakeComposerService_2_1>;
 
-    // The size should not have updated as we have not provided a new buffer.
-    auto referenceFrame1 = mBaseFrame;
-    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
-    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
-    restoreInitialState();
-
-    // Now we repeat with setGeometryAppliesWithResize
-    // and verify the position DOESN'T latch.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setSize(mFGSurfaceControl, 32, 32);
-        ts.setPosition(mFGSurfaceControl, 100, 100);
-    }
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
-    completeFGResize();
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
-    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
-    referenceFrame2[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(LatchingTest_2_1, DISABLED_SurfacePositionLatching) {
+    Test_SurfacePositionLatching();
 }
 
-TEST_F(LatchingTest, CropLatching) {
-    // Normally the crop applies immediately even while a resize is pending.
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
-    }
-
-    auto referenceFrame1 = mBaseFrame;
-    referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
-    referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
-    EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
-    restoreInitialState();
-
-    {
-        TransactionScope ts(*sFakeComposer);
-        ts.setSize(mFGSurfaceControl, 128, 128);
-        ts.setGeometryAppliesWithResize(mFGSurfaceControl);
-        ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
-    }
-    EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
-
-    completeFGResize();
-
-    auto referenceFrame2 = mBaseFrame;
-    referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
-    referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
-    referenceFrame2[FG_LAYER].mSwapCount++;
-    EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(LatchingTest_2_1, DISABLED_CropLatching) {
+    Test_CropLatching();
 }
 
 } // namespace
@@ -1387,8 +1999,11 @@
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
 
-    sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
+    auto* fakeEnvironment = new sftest::FakeHwcEnvironment;
     ::testing::AddGlobalTestEnvironment(fakeEnvironment);
     ::testing::InitGoogleMock(&argc, argv);
     return RUN_ALL_TESTS();
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
index 13774b4..f9a1fe9 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <array>
 #include <unordered_set>
 #include <unordered_map>
@@ -4770,3 +4774,6 @@
     ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
             virtualDisplayExceptions));
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
index 6484562..fcd0d31 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <mutex>
 #include <array>
 #include <sstream>
@@ -789,3 +793,6 @@
 
     return 0;
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
index 90127a1..b76ace8 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestLayers.cpp
@@ -13,6 +13,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <sstream>
 #include <gtest/gtest.h>
 
@@ -279,3 +283,6 @@
 
     return optimized;
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp
index 904b927..8ca8815 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <sstream>
 #include <android/hardware/graphics/common/1.0/types.h>
 
@@ -111,3 +115,6 @@
     }
     return stream.str();
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
index c5b92d0..1efb21e 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <sstream>
 #include <cutils/log.h>
 #include <ui/Rect.h>
@@ -780,3 +784,6 @@
 const std::array<bool, 6> Hwc2TestTransform::mCompositionSupport = {{
     false,   true,    true,    false,   true,    true,
 }};
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f842d61..1cd8731 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -36,26 +36,35 @@
         ":libsurfaceflinger_sources",
         "libsurfaceflinger_unittest_main.cpp",
         "CachingTest.cpp",
-	"CompositionTest.cpp",
+        "CompositionTest.cpp",
         "DispSyncSourceTest.cpp",
         "DisplayIdentificationTest.cpp",
         "DisplayTransactionTest.cpp",
         "EventControlThreadTest.cpp",
         "EventThreadTest.cpp",
-        "IdleTimerTest.cpp",
+        "HWComposerTest.cpp",
+        "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
+        "LayerHistoryTestV2.cpp",
         "LayerMetadataTest.cpp",
+        "PhaseOffsetsTest.cpp",
         "SchedulerTest.cpp",
         "SchedulerUtilsTest.cpp",
         "RefreshRateConfigsTest.cpp",
+        "RefreshRateSelectionTest.cpp",
         "RefreshRateStatsTest.cpp",
         "RegionSamplingTest.cpp",
         "TimeStatsTest.cpp",
+        "FrameTracerTest.cpp",
+        "TransactionApplicationTest.cpp",
+        "StrongTypingTest.cpp",
+        "VSyncDispatchTimerQueueTest.cpp",
+        "VSyncDispatchRealtimeTest.cpp",
+        "VSyncPredictorTest.cpp",
+        "VSyncReactorTest.cpp",
         "mock/DisplayHardware/MockComposer.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",
@@ -63,13 +72,24 @@
         "mock/MockNativeWindowSurface.cpp",
         "mock/MockSurfaceInterceptor.cpp",
         "mock/MockTimeStats.cpp",
+        "mock/MockFrameTracer.cpp",
         "mock/system/window/MockNativeWindow.cpp",
     ],
     static_libs: [
         "libgmock",
         "libcompositionengine",
         "libcompositionengine_mocks",
+        "libgui_mocks",
+        "libperfetto_client_experimental",
         "librenderengine_mocks",
+        "perfetto_trace_protos",
+    ],
+    shared_libs: [
+        "libprotoutil",
+        "libstatssocket",
+        "libsurfaceflinger",
+        "libtimestats",
+        "libtimestats_proto",
     ],
     header_libs: [
         "libsurfaceflinger_headers",
diff --git a/services/surfaceflinger/tests/unittests/CachingTest.cpp b/services/surfaceflinger/tests/unittests/CachingTest.cpp
index 74ce540..1b8c76d 100644
--- a/services/surfaceflinger/tests/unittests/CachingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CachingTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "CachingTest"
 
@@ -91,3 +95,6 @@
     }
 }
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 4f8ed1a..06ef8e7 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "CompositionTest"
 
@@ -31,16 +35,15 @@
 #include <utils/String8.h>
 
 #include "BufferQueueLayer.h"
-#include "ColorLayer.h"
+#include "EffectLayer.h"
 #include "Layer.h"
-
-#include "TestableScheduler.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/MockTimeStats.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -95,16 +98,13 @@
         mFlinger.mutableEventQueue().reset(mMessageQueue);
         setupScheduler();
 
-        EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
-        EXPECT_CALL(*mPrimaryDispSync, getPeriod())
-                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
-        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
         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));
+        mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
         setupComposer(0);
     }
 
@@ -116,8 +116,6 @@
 
     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));
 
@@ -125,15 +123,31 @@
     }
 
     void setupScheduler() {
-        mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
-        mScheduler->mutableEventControlThread().reset(mEventControlThread);
-        mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
-        EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_));
-        sp<Scheduler::ConnectionHandle> connectionHandle =
-                mScheduler->addConnection(std::move(mEventThread));
-        mFlinger.mutableSfConnectionHandle() = std::move(connectionHandle);
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
 
-        mFlinger.mutableScheduler().reset(mScheduler);
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+        EXPECT_CALL(*primaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*primaryDispSync, getPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+        EXPECT_CALL(*primaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+
+        mFlinger.setupScheduler(std::move(primaryDispSync),
+                                std::make_unique<mock::EventControlThread>(),
+                                std::move(eventThread), std::move(sfEventThread));
     }
 
     void setupForceGeometryDirty() {
@@ -157,7 +171,7 @@
 
     std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream};
 
-    TestableScheduler* mScheduler;
+    bool mDisplayOff = false;
     TestableSurfaceFlinger mFlinger;
     sp<DisplayDevice> mDisplay;
     sp<DisplayDevice> mExternalDisplay;
@@ -168,13 +182,10 @@
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
     ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer();
 
-    std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
-    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
-
     Hwc2::mock::Composer* mComposer = nullptr;
     renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+    mock::TimeStats* mTimeStats = new mock::TimeStats();
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
 
     sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
 
@@ -273,13 +284,14 @@
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
         EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
-        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 =
+                FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
+                                          DisplayConnectionType::Internal, true /* isPrimary */)
+                        .setDisplaySurface(test->mDisplaySurface)
+                        .setNativeWindow(test->mNativeWindow)
+                        .setSecure(Derived::IS_SECURE)
+                        .setPowerMode(Derived::INIT_POWER_MODE)
+                        .inject();
         Mock::VerifyAndClear(test->mNativeWindow);
         test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK);
     }
@@ -296,18 +308,12 @@
         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);
@@ -321,8 +327,9 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly(
                         [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<renderengine::LayerSettings>&, ANativeWindowBuffer*,
-                           const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+                           const std::vector<const renderengine::LayerSettings*>&,
+                           ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                           base::unique_fd*) -> status_t {
                             EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                             EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                                       displaySettings.physicalDisplay);
@@ -341,14 +348,24 @@
     }
 
     static void setupHwcCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+
         EXPECT_CALL(*test->mDisplaySurface,
                     prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
                 .Times(1);
     }
 
+    static void setupHwcClientCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+    }
+
+    static void setupHwcForcedClientCompositionCallExpectations(CompositionTest* test) {
+        EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _)).Times(1);
+    }
+
     static void setupRECompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mDisplaySurface,
-                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
+                    prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU))
                 .Times(1);
         EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
                 .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -360,8 +377,9 @@
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillRepeatedly(
                         [](const renderengine::DisplaySettings& displaySettings,
-                           const std::vector<renderengine::LayerSettings>&, ANativeWindowBuffer*,
-                           const bool, base::unique_fd&&, base::unique_fd*) -> status_t {
+                           const std::vector<const renderengine::LayerSettings*>&,
+                           ANativeWindowBuffer*, const bool, base::unique_fd&&,
+                           base::unique_fd*) -> status_t {
                             EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
                             EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
                                       displaySettings.physicalDisplay);
@@ -419,6 +437,8 @@
     }
 
     static void setupHwcCompositionCallExpectations(CompositionTest*) {}
+    static void setupHwcClientCompositionCallExpectations(CompositionTest*) {}
+    static void setupHwcForcedClientCompositionCallExpectations(CompositionTest*) {}
 
     static void setupRECompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
@@ -507,7 +527,7 @@
 
         EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true));
         bool ignoredRecomputeVisibleRegions;
-        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0);
+        layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
         Mock::VerifyAndClear(test->mRenderEngine);
     }
 
@@ -522,69 +542,85 @@
     }
 
     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);
+        if (!test->mDisplayOff) {
+            // 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();
+            // 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);
+        if (!test->mDisplayOff) {
+            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);
+        if (!test->mDisplayOff) {
+            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);
+        if (!test->mDisplayOff) {
+            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);
+        if (!test->mDisplayOff) {
+            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);
+            // 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);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer, setLayerSurfaceDamage(HWC_DISPLAY, HWC_LAYER, _))
+                    .Times(1);
+            EXPECT_CALL(*test->mComposer, setLayerBuffer(HWC_DISPLAY, HWC_LAYER, _, _, _)).Times(1);
+        }
 
         setupBufferLayerPostFrameCallExpectations(test);
     }
@@ -592,7 +628,7 @@
     static void setupREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
                              ANativeWindowBuffer*, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
@@ -602,16 +638,22 @@
                               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(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);
+                    if (layerSettings.empty()) {
+                        ADD_FAILURE() << "layerSettings was not expected to be empty in "
+                                         "setupREBufferCompositionCommonCallExpectations "
+                                         "verification lambda";
+                        return NO_ERROR;
+                    }
+                    const renderengine::LayerSettings* layer = layerSettings.back();
+                    EXPECT_THAT(layer->source.buffer.buffer, Not(IsNull()));
+                    EXPECT_THAT(layer->source.buffer.fence, Not(IsNull()));
+                    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;
                 });
     }
@@ -635,7 +677,7 @@
     static void setupREColorCompositionCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
                              ANativeWindowBuffer*, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
@@ -645,14 +687,20 @@
                               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());
+                    if (layerSettings.empty()) {
+                        ADD_FAILURE()
+                                << "layerSettings was not expected to be empty in "
+                                   "setupREColorCompositionCallExpectations verification lambda";
+                        return NO_ERROR;
+                    }
+                    const 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);
+                              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;
                 });
     }
@@ -664,7 +712,9 @@
 
 struct DefaultLayerProperties : public BaseLayerProperties<DefaultLayerProperties> {};
 
-struct ColorLayerProperties : public BaseLayerProperties<ColorLayerProperties> {};
+struct EffectLayerProperties : public BaseLayerProperties<EffectLayerProperties> {
+    static constexpr IComposerClient::BlendMode BLENDMODE = IComposerClient::BlendMode::NONE;
+};
 
 struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerProperties> {
     using Base = BaseLayerProperties<SidebandLayerProperties>;
@@ -705,7 +755,7 @@
     static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) {
         EXPECT_CALL(*test->mRenderEngine, drawLayers)
                 .WillOnce([](const renderengine::DisplaySettings& displaySettings,
-                             const std::vector<renderengine::LayerSettings>& layerSettings,
+                             const std::vector<const renderengine::LayerSettings*>& layerSettings,
                              ANativeWindowBuffer*, const bool, base::unique_fd&&,
                              base::unique_fd*) -> status_t {
                     EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
@@ -715,12 +765,18 @@
                               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);
+                    if (layerSettings.empty()) {
+                        ADD_FAILURE() << "layerSettings was not expected to be empty in "
+                                         "setupInsecureREBufferCompositionCommonCallExpectations "
+                                         "verification lambda";
+                        return NO_ERROR;
+                    }
+                    const 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;
                 });
     }
@@ -763,6 +819,9 @@
 
         sp<L> layer = factory();
 
+        // Layer should be registered with scheduler.
+        EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+
         Mock::VerifyAndClear(test->mComposer);
         Mock::VerifyAndClear(test->mRenderEngine);
         Mock::VerifyAndClear(test->mMessageQueue);
@@ -773,8 +832,7 @@
         layerDrawingState.active.h = 100;
         layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1],
                                         LayerProperties::COLOR[2], LayerProperties::COLOR[3]);
-        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform());
-        layer->setVisibleRegion(Region(Rect(0, 0, 100, 100)));
+        layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */);
 
         return layer;
     }
@@ -783,19 +841,13 @@
         EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _))
                 .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE)));
 
-        std::vector<std::unique_ptr<compositionengine::OutputLayer>> outputLayers;
-        outputLayers.emplace_back(test->mDisplay->getCompositionDisplay()
-                                          ->getOrCreateOutputLayer(DEFAULT_DISPLAY_ID,
-                                                                   layer->getCompositionLayer(),
-                                                                   layer));
-
-        test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(std::move(outputLayers));
+        auto outputLayer = test->mDisplay->getCompositionDisplay()->injectOutputLayerForTest(
+                layer->getCompositionEngineLayerFE());
+        outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100));
+        outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100));
 
         Mock::VerifyAndClear(test->mComposer);
 
-        Vector<sp<Layer>> layers;
-        layers.add(layer);
-        test->mDisplay->setVisibleLayersSortedByZ(layers);
         test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer);
     }
 
@@ -803,23 +855,26 @@
         EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER))
                 .WillOnce(Return(Error::NONE));
 
-        test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(
-                std::vector<std::unique_ptr<compositionengine::OutputLayer>>());
+        test->mDisplay->getCompositionDisplay()->clearOutputLayers();
         test->mFlinger.mutableDrawingState().layersSortedByZ.clear();
+
+        // Layer should be unregistered with scheduler.
+        test->mFlinger.onMessageReceived(MessageQueue::INVALIDATE);
+        EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
     }
 };
 
 template <typename LayerProperties>
-struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> {
+struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> {
     using Base = BaseLayerVariant<LayerProperties>;
-    using FlingerLayerType = sp<ColorLayer>;
+    using FlingerLayerType = sp<EffectLayer>;
 
     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, LayerMetadata()));
+        FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
+            return new EffectLayer(
+                    LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), "test-layer",
+                                      LayerProperties::WIDTH, LayerProperties::HEIGHT,
+                                      LayerProperties::LAYER_FLAGS, LayerMetadata()));
         });
 
         auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer);
@@ -856,11 +911,12 @@
 
         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, LayerMetadata()));
+                    sp<Client> client;
+                    LayerCreationArgs args(test->mFlinger.mFlinger.get(), client, "test-layer",
+                                           LayerProperties::WIDTH, LayerProperties::HEIGHT,
+                                           LayerProperties::LAYER_FLAGS, LayerMetadata());
+                    args.textureName = test->mFlinger.mutableTexturePool().back();
+                    return new BufferQueueLayer(args);
                 });
 
         LayerProperties::setupLayerState(test, layer);
@@ -917,9 +973,11 @@
     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);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -933,9 +991,11 @@
     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);
+        if (!test->mDisplayOff) {
+            EXPECT_CALL(*test->mComposer,
+                        setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType))
+                    .Times(1);
+        }
     }
 
     static void setupHwcGetCallExpectations(CompositionTest* test) {
@@ -986,14 +1046,45 @@
     template <typename Case>
     static void setupCallExpectations(CompositionTest* test) {
         Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcClientCompositionCallExpectations(test);
         Case::Display::setupRECompositionCallExpectations(test);
         Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
     }
 };
 
-struct ForcedClientCompositionResultVariant : public RECompositionResultVariant {
+struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant {
     static void setupLayerState(CompositionTest* test, sp<Layer> layer) {
-        layer->forceClientComposition(test->mDisplay);
+        const auto outputLayer = layer->findOutputLayerForDisplay(test->mDisplay);
+        LOG_FATAL_IF(!outputLayer);
+        outputLayer->editState().forceClientComposition = true;
+    }
+
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcForcedClientCompositionCallExpectations(test);
+        Case::Display::setupRECompositionCallExpectations(test);
+        Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
+    }
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {}
+
+    template <typename Case>
+    static void setupCallExpectationsForDirtyFrame(CompositionTest*) {}
+};
+
+struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionResultBaseVariant {
+    static void setupLayerState(CompositionTest* test, sp<Layer>) {
+        test->mFlinger.mutableDebugDisableHWC() = true;
+    }
+
+    template <typename Case>
+    static void setupCallExpectations(CompositionTest* test) {
+        Case::Display::setupNonEmptyFrameCompositionCallExpectations(test);
+        Case::Display::setupHwcForcedClientCompositionCallExpectations(test);
+        Case::Display::setupRECompositionCallExpectations(test);
+        Case::Display::template setupRELayerCompositionCallExpectations<Case>(test);
     }
 
     template <typename Case>
@@ -1070,11 +1161,11 @@
     static void cleanup(CompositionTest* test) {
         Layer::cleanupInjectedLayers(test);
 
-        for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) {
-            hwcDisplay->mutableLayers().clear();
+        for (auto& displayData : test->mFlinger.mutableHwcDisplayData()) {
+            static_cast<TestableSurfaceFlinger::HWC2Display*>(displayData.second.hwcDisplay.get())
+                    ->mutableLayers()
+                    .clear();
         }
-
-        test->mDisplay->setVisibleLayersSortedByZ(Vector<sp<android::Layer>>());
     }
 };
 
@@ -1136,31 +1227,31 @@
  *  Single-color layers
  */
 
-TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyGeometry) {
+TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyGeometry) {
     displayRefreshCompositionDirtyGeometry<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
                             HwcCompositionResultVariant>>();
 }
 
-TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyFrame) {
+TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyFrame) {
     displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
                             HwcCompositionResultVariant>>();
 }
 
-TEST_F(CompositionTest, REComposedColorLayer) {
+TEST_F(CompositionTest, REComposedEffectLayer) {
     displayRefreshCompositionDirtyFrame<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR,
                                                          IComposerClient::Composition::CLIENT>,
                             RECompositionResultVariant>>();
 }
 
-TEST_F(CompositionTest, captureScreenColorLayer) {
+TEST_F(CompositionTest, captureScreenEffectLayer) {
     captureScreenComposition<
-            CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>,
+            CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
                             NoCompositionTypeVariant, REScreenshotResultVariant>>();
 }
 
@@ -1289,6 +1380,7 @@
  */
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyGeometry<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1296,6 +1388,7 @@
 }
 
 TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
@@ -1303,6 +1396,7 @@
 }
 
 TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) {
+    mDisplayOff = true;
     displayRefreshCompositionDirtyFrame<CompositionCase<
             PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
             ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
@@ -1316,5 +1410,26 @@
             NoCompositionTypeVariant, REScreenshotResultVariant>>();
 }
 
+/* ------------------------------------------------------------------------
+ *  Client composition forced through debug/developer settings
+ */
+
+TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyGeometry) {
+    displayRefreshCompositionDirtyGeometry<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionViaDebugOptionResultVariant>>();
+}
+
+TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyFrame) {
+    displayRefreshCompositionDirtyFrame<
+            CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+                            KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
+                            ForcedClientCompositionViaDebugOptionResultVariant>>();
+}
+
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0aa8cf5..2e705da 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -51,7 +51,6 @@
     AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
 
     static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
-    static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
     static constexpr int mIterations = 100;
 };
 
@@ -79,8 +78,7 @@
 
 void DispSyncSourceTest::createDispSyncSource() {
     createDispSync();
-    mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
-                                                       mOffsetThresholdForNextVsync.count(), true,
+    mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
                                                        "DispSyncSourceTest");
     mDispSyncSource->setCallback(this);
 }
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 5f58e7d..4da0647 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
@@ -21,9 +25,12 @@
 
 #include <compositionengine/Display.h>
 #include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/OutputCompositionState.h>
 #include <compositionengine/mock/DisplaySurface.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <gui/mock/GraphicBufferConsumer.h>
+#include <gui/mock/GraphicBufferProducer.h>
 #include <log/log.h>
 #include <renderengine/mock/RenderEngine.h>
 #include <ui/DebugUtils.h>
@@ -38,8 +45,6 @@
 #include "mock/MockMessageQueue.h"
 #include "mock/MockNativeWindowSurface.h"
 #include "mock/MockSurfaceInterceptor.h"
-#include "mock/gui/MockGraphicBufferConsumer.h"
-#include "mock/gui/MockGraphicBufferProducer.h"
 #include "mock/system/window/MockNativeWindow.h"
 
 namespace android {
@@ -95,11 +100,10 @@
     DisplayTransactionTest();
     ~DisplayTransactionTest() override;
 
-    void setupScheduler();
-
     // --------------------------------------------------------------------
     // Mock/Fake injection
 
+    void injectMockScheduler();
     void injectMockComposer(int virtualDisplayCount);
     void injectFakeBufferQueueFactory();
     void injectFakeNativeWindowSurfaceFactory();
@@ -119,11 +123,7 @@
     // --------------------------------------------------------------------
     // Test instances
 
-    TestableScheduler* mScheduler;
     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();
 
@@ -134,7 +134,11 @@
     Hwc2::mock::Composer* mComposer = nullptr;
     mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
     mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor();
-    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+
+    mock::DispSync* mPrimaryDispSync = new mock::DispSync;
+    mock::EventControlThread* mEventControlThread = new mock::EventControlThread;
+    mock::EventThread* mEventThread = new mock::EventThread;
+    mock::EventThread* mSFEventThread = new mock::EventThread;
 
     // These mocks are created only when expected to be created via a factory.
     sp<mock::GraphicBufferConsumer> mConsumer;
@@ -150,7 +154,7 @@
     // Default to no wide color display support configured
     mFlinger.mutableHasWideColorDisplay() = false;
     mFlinger.mutableUseColorManagement() = false;
-    mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
+    mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
 
     // Default to using HWC virtual displays
     mFlinger.mutableUseHwcVirtualDisplays() = true;
@@ -164,7 +168,7 @@
         return nullptr;
     });
 
-    setupScheduler();
+    injectMockScheduler();
     mFlinger.mutableEventQueue().reset(mMessageQueue);
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
@@ -178,26 +182,25 @@
     ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
 }
 
-void DisplayTransactionTest::setupScheduler() {
-    mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs());
-    mScheduler->mutableEventControlThread().reset(mEventControlThread);
-    mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync);
+void DisplayTransactionTest::injectMockScheduler() {
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
-    EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
 
-    sp<Scheduler::ConnectionHandle> sfConnectionHandle =
-            mScheduler->addConnection(std::unique_ptr<EventThread>(mSFEventThread));
-    mFlinger.mutableSfConnectionHandle() = std::move(sfConnectionHandle);
-    sp<Scheduler::ConnectionHandle> appConnectionHandle =
-            mScheduler->addConnection(std::unique_ptr<EventThread>(mEventThread));
-    mFlinger.mutableAppConnectionHandle() = std::move(appConnectionHandle);
-    mFlinger.mutableScheduler().reset(mScheduler);
+    EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync),
+                            std::unique_ptr<EventControlThread>(mEventControlThread),
+                            std::unique_ptr<EventThread>(mEventThread),
+                            std::unique_ptr<EventThread>(mSFEventThread));
 }
 
 void DisplayTransactionTest::injectMockComposer(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));
 
@@ -307,6 +310,16 @@
     static std::optional<DisplayId> get() { return {}; }
 };
 
+template <typename>
+struct DisplayConnectionTypeGetter {
+    static constexpr std::optional<DisplayConnectionType> value;
+};
+
+template <typename PhysicalDisplay>
+struct DisplayConnectionTypeGetter<PhysicalDisplayId<PhysicalDisplay>> {
+    static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE;
+};
+
 // 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.
@@ -315,6 +328,7 @@
           Secure secure, Primary primary, int grallocUsage>
 struct DisplayVariant {
     using DISPLAY_ID = DisplayIdGetter<DisplayIdType>;
+    using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>;
 
     // The display width and height
     static constexpr int WIDTH = width;
@@ -340,8 +354,8 @@
 
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
         auto injector =
-                FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(),
-                                          static_cast<bool>(VIRTUAL), static_cast<bool>(PRIMARY));
+                FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(), CONNECTION_TYPE::value,
+                                          static_cast<bool>(PRIMARY));
 
         injector.setSecure(static_cast<bool>(SECURE));
         injector.setNativeWindow(test->mNativeWindow);
@@ -445,10 +459,15 @@
     }
 
     static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
-        EXPECT_CALL(*test->mComposer, getDisplayType(HWC_DISPLAY_ID, _))
-                .WillOnce(DoAll(SetArgPointee<1>(static_cast<IComposerClient::DisplayType>(
-                                        HWC_DISPLAY_TYPE)),
-                                Return(Error::NONE)));
+        constexpr auto CONNECTION_TYPE =
+                PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
+                ? IComposerClient::DisplayConnectionType::INTERNAL
+                : IComposerClient::DisplayConnectionType::EXTERNAL;
+
+        EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _))
+                .WillOnce(
+                        DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(Hwc2::V2_4::Error::NONE)));
+
         EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE));
         EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _))
                 .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}),
@@ -473,6 +492,10 @@
                     getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
                                         IComposerClient::Attribute::DPI_Y, _))
                 .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+        EXPECT_CALL(*test->mComposer,
+                    getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+                                        IComposerClient::Attribute::CONFIG_GROUP, _))
+                .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
 
         if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
             EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
@@ -509,6 +532,7 @@
 
 template <bool hasIdentificationData>
 struct PrimaryDisplay {
+    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal;
     static constexpr Primary PRIMARY = Primary::TRUE;
     static constexpr uint8_t PORT = 255;
     static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
@@ -517,6 +541,7 @@
 
 template <bool hasIdentificationData>
 struct ExternalDisplay {
+    static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External;
     static constexpr Primary PRIMARY = Primary::FALSE;
     static constexpr uint8_t PORT = 254;
     static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData;
@@ -598,7 +623,7 @@
     static void injectConfigChange(DisplayTransactionTest* test) {
         test->mFlinger.mutableHasWideColorDisplay() = false;
         test->mFlinger.mutableUseColorManagement() = false;
-        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
+        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
@@ -618,7 +643,7 @@
     static void injectConfigChange(DisplayTransactionTest* test) {
         test->mFlinger.mutableUseColorManagement() = true;
         test->mFlinger.mutableHasWideColorDisplay() = true;
-        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED;
+        test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged;
     }
 
     static void setupComposerCallExpectations(DisplayTransactionTest* test) {
@@ -1131,8 +1156,8 @@
     // Preconditions
 
     // vsync is enabled and available
-    mScheduler->mutablePrimaryHWVsyncEnabled() = true;
-    mScheduler->mutableHWVsyncAvailable() = true;
+    mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = true;
+    mFlinger.scheduler()->mutableHWVsyncAvailable() = true;
 
     // A display exists
     auto existing = Case::Display::makeFakeExistingDisplayInjector(this);
@@ -1156,8 +1181,8 @@
     // Postconditions
 
     // vsyncs should be off and not available.
-    EXPECT_FALSE(mScheduler->mutablePrimaryHWVsyncEnabled());
-    EXPECT_FALSE(mScheduler->mutableHWVsyncAvailable());
+    EXPECT_FALSE(mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled());
+    EXPECT_FALSE(mFlinger.scheduler()->mutableHWVsyncAvailable());
 
     // The display should have been removed from the display map.
     EXPECT_FALSE(hasDisplayDevice(existing.token()));
@@ -1178,7 +1203,8 @@
 
     GetBestColorModeTest()
           : DisplayTransactionTest(),
-            mInjector(FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID, false /* isVirtual */,
+            mInjector(FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID,
+                                                DisplayConnectionType::Internal,
                                                 true /* isPrimary */)) {}
 
     void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
@@ -1274,6 +1300,245 @@
 }
 
 /* ------------------------------------------------------------------------
+ * DisplayDevice::setProjection
+ */
+
+class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
+public:
+    static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+    static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080;  // arbitrary
+    static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
+
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V;
+    static constexpr int32_t TRANSFORM_FLAGS_ROT_270 =
+            HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90;
+
+    DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize,
+                                   ui::Rotation physicalOrientation)
+          : mFlingerDisplaySize(flingerDisplaySize),
+            mHardwareDisplaySize(hardwareDisplaySize),
+            mPhysicalOrientation(physicalOrientation),
+            mDisplayDevice(createDisplayDevice()) {}
+
+    sp<DisplayDevice> createDisplayDevice() {
+        // The DisplayDevice is required to have a framebuffer (behind the
+        // ANativeWindow interface) which uses the actual hardware display
+        // size.
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.width), Return(0)));
+        EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+                .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.height), Return(0)));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+        EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT));
+
+        return FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID,
+                                         DisplayConnectionType::Internal, true /* isPrimary */)
+                .setNativeWindow(mNativeWindow)
+                .setPhysicalOrientation(mPhysicalOrientation)
+                .inject();
+    }
+
+    ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
+
+    void setProjectionForRotation0() {
+        // A logical rotation of 0 uses the SurfaceFlinger display size
+        mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize),
+                                      Rect(mFlingerDisplaySize));
+    }
+
+    void setProjectionForRotation90() {
+        // A logical rotation of 90 uses the SurfaceFlinger display size with
+        // the width/height swapped.
+        mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)),
+                                      Rect(SwapWH(mFlingerDisplaySize)));
+    }
+
+    void setProjectionForRotation180() {
+        // A logical rotation of 180 uses the SurfaceFlinger display size
+        mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize),
+                                      Rect(mFlingerDisplaySize));
+    }
+
+    void setProjectionForRotation270() {
+        // A logical rotation of 270 uses the SurfaceFlinger display size with
+        // the width/height swapped.
+        mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)),
+                                      Rect(SwapWH(mFlingerDisplaySize)));
+    }
+
+    void expectStateForHardwareTransform0() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform90() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        // For 90, the frame and viewport have the hardware display size width and height swapped
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform180() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    void expectStateForHardwareTransform270() {
+        const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState();
+        EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width,
+                                mHardwareDisplaySize.height),
+                  compositionState.transform);
+        EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip);
+        EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip);
+        // For 270, the frame and viewport have the hardware display size width and height swapped
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame);
+        EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport);
+        EXPECT_EQ(false, compositionState.needsFiltering);
+    }
+
+    const ui::Size mFlingerDisplaySize;
+    const ui::Size mHardwareDisplaySize;
+    const ui::Rotation mPhysicalOrientation;
+    const sp<DisplayDevice> mDisplayDevice;
+};
+
+struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed0()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_0) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform270();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed90()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_90) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform0();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed180()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_180) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform180();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform90();
+}
+
+struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest {
+    DisplayDeviceSetProjectionTest_Installed270()
+          : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH),
+                                           ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+                                           ui::ROTATION_270) {}
+};
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) {
+    setProjectionForRotation0();
+    expectStateForHardwareTransform270();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) {
+    setProjectionForRotation90();
+    expectStateForHardwareTransform0();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) {
+    setProjectionForRotation180();
+    expectStateForHardwareTransform90();
+}
+
+TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) {
+    setProjectionForRotation270();
+    expectStateForHardwareTransform180();
+}
+
+/* ------------------------------------------------------------------------
  * SurfaceFlinger::getDisplayNativePrimaries
  */
 
@@ -1401,8 +1666,12 @@
     // Invocation
 
     DisplayDeviceState state;
-    state.displayId = static_cast<bool>(Case::Display::VIRTUAL) ? std::nullopt
-                                                                : Case::Display::DISPLAY_ID::get();
+    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+        const auto displayId = Case::Display::DISPLAY_ID::get();
+        ASSERT_TRUE(displayId);
+        state.physical = {*displayId, *connectionType};
+    }
+
     state.isSecure = static_cast<bool>(Case::Display::SECURE);
 
     auto device =
@@ -1414,6 +1683,7 @@
 
     ASSERT_TRUE(device != nullptr);
     EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId());
+    EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType());
     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());
@@ -1427,7 +1697,7 @@
     // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
     // remapped, and the test only ever sets up one config. If there were an error
     // looking up the remapped index, device->getActiveConfig() would be -1 instead.
-    EXPECT_EQ(0, device->getActiveConfig());
+    EXPECT_EQ(0, device->getActiveConfig().value());
     EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
               device->getSupportedPerFrameMetadata());
 }
@@ -1577,21 +1847,24 @@
     EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure());
     EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary());
 
+    std::optional<DisplayDeviceState::Physical> expectedPhysical;
+    if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+        const auto displayId = Case::Display::DISPLAY_ID::get();
+        ASSERT_TRUE(displayId);
+        expectedPhysical = {*displayId, *connectionType};
+    }
+
     // The display should have been set up in the current display state
     ASSERT_TRUE(hasCurrentDisplayState(displayToken));
     const auto& current = getCurrentDisplayState(displayToken);
     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);
+    EXPECT_EQ(expectedPhysical, current.physical);
 
     // The display should have been set up in the drawing display state
     ASSERT_TRUE(hasDrawingDisplayState(displayToken));
     const auto& draw = getDrawingDisplayState(displayToken);
     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);
+    EXPECT_EQ(expectedPhysical, draw.physical);
 }
 
 template <typename Case>
@@ -2040,8 +2313,8 @@
 TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) {
     using Case = NonHwcVirtualDisplayCase;
 
-    constexpr int oldTransform = 0;
-    constexpr int newTransform = 2;
+    constexpr ui::Rotation oldTransform = ui::ROTATION_0;
+    constexpr ui::Rotation newTransform = ui::ROTATION_180;
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2414,7 +2687,7 @@
 
 TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) {
     using Case = SimplePrimaryDisplayCase;
-    constexpr int initialOrientation = 180;
+    constexpr ui::Rotation initialOrientation = ui::ROTATION_180;
     const Rect initialFrame = {1, 2, 3, 4};
     const Rect initialViewport = {5, 6, 7, 8};
 
@@ -2458,8 +2731,8 @@
 
 TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) {
     using Case = SimplePrimaryDisplayCase;
-    constexpr int initialOrientation = 90;
-    constexpr int desiredOrientation = 180;
+    constexpr ui::Rotation initialOrientation = ui::ROTATION_90;
+    constexpr ui::Rotation desiredOrientation = ui::ROTATION_180;
 
     // --------------------------------------------------------------------
     // Preconditions
@@ -2721,7 +2994,7 @@
     // The layer stack state should be set to zero
     EXPECT_EQ(0u, primaryDisplayState.layerStack);
     // The orientation state should be set to zero
-    EXPECT_EQ(0, primaryDisplayState.orientation);
+    EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation);
 
     // The frame state should be set to INVALID
     EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame);
@@ -3008,7 +3281,7 @@
     }
 
     static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) {
-        test->mScheduler->mutablePrimaryHWVsyncEnabled() = enabled;
+        test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled;
     }
 
     static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
@@ -3239,3 +3512,6 @@
 
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index dbd9b84..65b3e35 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -19,13 +19,12 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
-
 #include <utils/Errors.h>
 
 #include "AsyncCallRecorder.h"
 #include "Scheduler/EventThread.h"
+#include "Scheduler/HwcStrongTypes.h"
 
 using namespace std::chrono_literals;
 using namespace std::placeholders;
@@ -34,6 +33,7 @@
 using testing::Invoke;
 
 namespace android {
+
 namespace {
 
 constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
@@ -42,6 +42,8 @@
 
 class MockVSyncSource : public VSyncSource {
 public:
+    const char* getName() const override { return "test"; }
+
     MOCK_METHOD1(setVSyncEnabled, void(bool));
     MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
     MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
@@ -54,8 +56,7 @@
 protected:
     class MockEventThreadConnection : public EventThreadConnection {
     public:
-        MockEventThreadConnection(android::impl::EventThread* eventThread,
-                                  ResyncCallback&& resyncCallback,
+        MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback,
                                   ISurfaceComposer::ConfigChanged configChanged)
               : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {}
         MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event));
@@ -67,7 +68,7 @@
     EventThreadTest();
     ~EventThreadTest() override;
 
-    void createThread();
+    void createThread(std::unique_ptr<VSyncSource>);
     sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder,
                                                    ISurfaceComposer::ConfigChanged configChanged);
 
@@ -82,7 +83,8 @@
     void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
                                                 bool expectedConnected);
     void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId,
-                                                      int32_t expectedConfigId);
+                                                      int32_t expectedConfigId,
+                                                      nsecs_t expectedVsyncPeriod);
 
     AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder;
     AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder;
@@ -91,9 +93,9 @@
     AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder;
     ConnectionEventRecorder mConnectionEventCallRecorder{0};
 
-    MockVSyncSource mVSyncSource;
+    MockVSyncSource* mVSyncSource;
     VSyncSource::Callback* mCallback = nullptr;
-    std::unique_ptr<android::impl::EventThread> mThread;
+    std::unique_ptr<impl::EventThread> mThread;
     sp<MockEventThreadConnection> mConnection;
 };
 
@@ -102,16 +104,19 @@
             ::testing::UnitTest::GetInstance()->current_test_info();
     ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
 
-    EXPECT_CALL(mVSyncSource, setVSyncEnabled(_))
+    auto vsyncSource = std::make_unique<MockVSyncSource>();
+    mVSyncSource = vsyncSource.get();
+
+    EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_))
             .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable()));
 
-    EXPECT_CALL(mVSyncSource, setCallback(_))
+    EXPECT_CALL(*mVSyncSource, setCallback(_))
             .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable()));
 
-    EXPECT_CALL(mVSyncSource, setPhaseOffset(_))
+    EXPECT_CALL(*mVSyncSource, setPhaseOffset(_))
             .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable()));
 
-    createThread();
+    createThread(std::move(vsyncSource));
     mConnection = createConnection(mConnectionEventCallRecorder,
                                    ISurfaceComposer::eConfigChangedDispatch);
 
@@ -129,11 +134,9 @@
     EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value());
 }
 
-void EventThreadTest::createThread() {
-    mThread =
-            std::make_unique<android::impl::EventThread>(&mVSyncSource,
-                                                         mInterceptVSyncCallRecorder.getInvocable(),
-                                                         "unit-test-event-thread");
+void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) {
+    mThread = std::make_unique<impl::EventThread>(std::move(source),
+                                                  mInterceptVSyncCallRecorder.getInvocable());
 
     // EventThread should register itself as VSyncSource callback.
     mCallback = expectVSyncSetCallbackCallReceived();
@@ -205,13 +208,15 @@
 }
 
 void EventThreadTest::expectConfigChangedEventReceivedByConnection(
-        PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId) {
+        PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId,
+        nsecs_t expectedVsyncPeriod) {
     auto args = mConnectionEventCallRecorder.waitForCall();
     ASSERT_TRUE(args.has_value());
     const auto& event = std::get<0>(args.value());
     EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type);
     EXPECT_EQ(expectedDisplayId, event.header.displayId);
     EXPECT_EQ(expectedConfigId, event.config.configId);
+    EXPECT_EQ(expectedVsyncPeriod, event.config.vsyncPeriod);
 }
 
 namespace {
@@ -394,6 +399,34 @@
     expectVSyncSetEnabledCallReceived(false);
 }
 
+TEST_F(EventThreadTest, tracksEventConnections) {
+    EXPECT_EQ(1, mThread->getEventThreadConnectionCount());
+    ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY};
+    sp<MockEventThreadConnection> errorConnection =
+            createConnection(errorConnectionEventRecorder,
+                             ISurfaceComposer::eConfigChangedSuppress);
+    mThread->setVsyncRate(1, errorConnection);
+    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+    ConnectionEventRecorder secondConnectionEventRecorder{0};
+    sp<MockEventThreadConnection> secondConnection =
+            createConnection(secondConnectionEventRecorder,
+                             ISurfaceComposer::eConfigChangedSuppress);
+    mThread->setVsyncRate(1, secondConnection);
+    EXPECT_EQ(3, mThread->getEventThreadConnectionCount());
+
+    // EventThread should enable vsync callbacks.
+    expectVSyncSetEnabledCallReceived(true);
+
+    // The first event will be seen by the interceptor, and by the connection,
+    // which then returns an error.
+    mCallback->onVSyncEvent(123);
+    expectInterceptCallReceived(123);
+    expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u);
+    expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123,
+                                         1u);
+    EXPECT_EQ(2, mThread->getEventThreadConnectionCount());
+}
+
 TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) {
     ConnectionEventRecorder errorConnectionEventRecorder{WOULD_BLOCK};
     sp<MockEventThreadConnection> errorConnection =
@@ -446,18 +479,18 @@
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary) {
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 7);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7);
+    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7), 16666666);
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedExternal) {
-    mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, 5);
-    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5);
+    mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5), 16666666);
+    expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5, 16666666);
 }
 
 TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
-    mThread->onConfigChanged(DISPLAY_ID_64BIT, 7);
-    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
+    mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7), 16666666);
+    expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7, 16666666);
 }
 
 TEST_F(EventThreadTest, suppressConfigChanged) {
@@ -466,8 +499,8 @@
             createConnection(suppressConnectionEventRecorder,
                              ISurfaceComposer::eConfigChangedSuppress);
 
-    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 9);
-    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9);
+    mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9), 16666666);
+    expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9, 16666666);
 
     auto args = suppressConnectionEventRecorder.waitForCall();
     ASSERT_FALSE(args.has_value());
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 1d75011..b50ddf5 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -20,42 +20,21 @@
 
 #include "Scheduler/PhaseOffsets.h"
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
+struct FakePhaseOffsets : PhaseConfiguration {
+    static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
 
-class FakePhaseOffsets : public android::scheduler::PhaseOffsets {
-    nsecs_t FAKE_PHASE_OFFSET_NS = 0;
+    Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
 
-public:
-    FakePhaseOffsets() = default;
-    ~FakePhaseOffsets() = default;
-
-    nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; }
-    nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; }
-
-    PhaseOffsets::Offsets getOffsetsForRefreshRate(
-            RefreshRateType /*refreshRateType*/) const override {
-        return getCurrentOffsets();
+    Offsets getCurrentOffsets() const override {
+        return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+                {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
     }
 
-    // Returns early, early GL, and late offsets for Apps and SF.
-    PhaseOffsets::Offsets getCurrentOffsets() const override {
-        return Offsets{{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                       {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
-                       {RefreshRateType::DEFAULT, 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(RefreshRateType /*refreshRateType*/) override {}
-
-    nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; }
-
-    // Returns current offsets in human friendly format.
-    void dump(std::string& /*result*/) const override {}
+    void setRefreshRateFps(float) override {}
+    void dump(std::string&) const override {}
 };
 
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
new file mode 100644
index 0000000..68cb52f
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -0,0 +1,403 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <FrameTracer/FrameTracer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <perfetto/trace/trace.pb.h>
+
+#include "libsurfaceflinger_unittest_main.h"
+
+using namespace google::protobuf;
+
+namespace android {
+namespace {
+
+class FrameTracerTest : public testing::Test {
+public:
+    FrameTracerTest() {
+        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());
+
+        // Need to initialize tracing in process for testing, and only once per test suite.
+        static bool wasInitialized = false;
+        if (!wasInitialized) {
+            perfetto::TracingInitArgs args;
+            args.backends = perfetto::kInProcessBackend;
+            perfetto::Tracing::Initialize(args);
+            wasInitialized = true;
+        }
+    }
+
+    ~FrameTracerTest() {
+        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 SetUp() override {
+        mFrameTracer = std::make_unique<FrameTracer>();
+        mFrameTracer->registerDataSource();
+    }
+
+    void TearDown() override { mFrameTracer.reset(); }
+
+    // Each tracing session can be used for a single block of Start -> Stop.
+    static std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest() {
+        perfetto::TraceConfig cfg;
+        cfg.set_duration_ms(500);
+        cfg.add_buffers()->set_size_kb(1024);
+        auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+        ds_cfg->set_name(FrameTracer::kFrameTracerDataSource);
+
+        auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend);
+        tracingSession->Setup(cfg);
+        return tracingSession;
+    }
+
+    std::unique_ptr<FrameTracer> mFrameTracer;
+    FenceToFenceTimeMap fenceFactory;
+};
+
+TEST_F(FrameTracerTest, traceNewLayerStartsTrackingLayerWhenTracing) {
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    mFrameTracer->traceNewLayer(layerId, layerName);
+
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+
+    auto tracingSession = getTracingSessionForTest();
+    tracingSession->StartBlocking();
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
+    tracingSession->StopBlocking();
+}
+
+TEST_F(FrameTracerTest, onDestroyRemovesTheTrackedLayer) {
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const int32_t secondlayerId = 6;
+
+    auto tracingSession = getTracingSessionForTest();
+    tracingSession->StartBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    mFrameTracer->traceNewLayer(secondlayerId, layerName);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 2\n");
+    tracingSession->StopBlocking();
+
+    mFrameTracer->onDestroy(layerId);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
+    mFrameTracer->onDestroy(layerId);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n");
+    mFrameTracer->onDestroy(secondlayerId);
+    EXPECT_EQ(mFrameTracer->miniDump(),
+              "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n");
+}
+
+TEST_F(FrameTracerTest, canTraceAfterAddingLayer) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 1;
+    const uint32_t bufferID = 2;
+    const uint64_t frameNumber = 3;
+    const nsecs_t timestamp = 4;
+    const nsecs_t duration = 5;
+    const auto type = FrameTracer::FrameEvent::POST;
+
+    {
+        auto tracingSession = getTracingSessionForTest();
+
+        tracingSession->StartBlocking();
+        // Clean up irrelevant traces.
+        tracingSession->ReadTraceBlocking();
+
+        mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
+        // Create second trace packet to finalize the previous one.
+        mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        EXPECT_EQ(raw_trace.size(), 0);
+    }
+
+    {
+        auto tracingSession = getTracingSessionForTest();
+
+        tracingSession->StartBlocking();
+        // Clean up irrelevant traces.
+        tracingSession->ReadTraceBlocking();
+
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration);
+        // Create second trace packet to finalize the previous one.
+        mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        ASSERT_GT(raw_trace.size(), 0);
+
+        perfetto::protos::Trace trace;
+        ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+        ASSERT_FALSE(trace.packet().empty());
+        EXPECT_EQ(trace.packet().size(), 1);
+
+        const auto& packet = trace.packet().Get(0);
+        ASSERT_TRUE(packet.has_timestamp());
+        EXPECT_EQ(packet.timestamp(), timestamp);
+        ASSERT_TRUE(packet.has_graphics_frame_event());
+        const auto& frame_event = packet.graphics_frame_event();
+        ASSERT_TRUE(frame_event.has_buffer_event());
+        const auto& buffer_event = frame_event.buffer_event();
+        ASSERT_TRUE(buffer_event.has_buffer_id());
+        EXPECT_EQ(buffer_event.buffer_id(), bufferID);
+        ASSERT_TRUE(buffer_event.has_frame_number());
+        EXPECT_EQ(buffer_event.frame_number(), frameNumber);
+        ASSERT_TRUE(buffer_event.has_type());
+        EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
+        ASSERT_TRUE(buffer_event.has_duration_ns());
+        EXPECT_EQ(buffer_event.duration_ns(), duration);
+    }
+}
+
+TEST_F(FrameTracerTest, traceFenceTriggersOnNextTraceAfterFenceFired) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+
+    {
+        auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+        fenceFactory.signalAllForTest(Fence::NO_FENCE, Fence::SIGNAL_TIME_PENDING);
+        auto tracingSession = getTracingSessionForTest();
+        tracingSession->StartBlocking();
+        // Clean up irrelevant traces.
+        tracingSession->ReadTraceBlocking();
+        // Trace.
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
+        // Create extra trace packet to (hopefully not) trigger and finalize the fence packet.
+        mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        EXPECT_EQ(raw_trace.size(), 0);
+    }
+
+    {
+        auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+        auto tracingSession = getTracingSessionForTest();
+        tracingSession->StartBlocking();
+        // Clean up irrelevant traces.
+        tracingSession->ReadTraceBlocking();
+        mFrameTracer->traceNewLayer(layerId, layerName);
+        mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type);
+        const nsecs_t timestamp = systemTime();
+        fenceFactory.signalAllForTest(Fence::NO_FENCE, timestamp);
+        // Create extra trace packet to trigger and finalize fence trace packets.
+        mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+        tracingSession->StopBlocking();
+
+        std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+        ASSERT_GT(raw_trace.size(), 0);
+
+        perfetto::protos::Trace trace;
+        ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+        ASSERT_FALSE(trace.packet().empty());
+        EXPECT_EQ(trace.packet().size(), 2); // Two packets because of the extra trace made above.
+
+        const auto& packet = trace.packet().Get(1);
+        ASSERT_TRUE(packet.has_timestamp());
+        EXPECT_EQ(packet.timestamp(), timestamp);
+        ASSERT_TRUE(packet.has_graphics_frame_event());
+        const auto& frame_event = packet.graphics_frame_event();
+        ASSERT_TRUE(frame_event.has_buffer_event());
+        const auto& buffer_event = frame_event.buffer_event();
+        ASSERT_TRUE(buffer_event.has_buffer_id());
+        EXPECT_EQ(buffer_event.buffer_id(), bufferID);
+        ASSERT_TRUE(buffer_event.has_frame_number());
+        EXPECT_EQ(buffer_event.frame_number(), frameNumber);
+        ASSERT_TRUE(buffer_event.has_type());
+        EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
+        EXPECT_FALSE(buffer_event.has_duration_ns());
+    }
+}
+
+TEST_F(FrameTracerTest, traceFenceWithStartTimeAfterSignalTime_ShouldHaveNoDuration) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+
+    auto tracingSession = getTracingSessionForTest();
+
+    tracingSession->StartBlocking();
+    // Clean up irrelevant traces.
+    tracingSession->ReadTraceBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+
+    // traceFence called after fence signalled.
+    const nsecs_t signalTime1 = systemTime();
+    const nsecs_t startTime1 = signalTime1 + 100000;
+    auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
+
+    // traceFence called before fence signalled.
+    const nsecs_t signalTime2 = systemTime();
+    const nsecs_t startTime2 = signalTime2 + 100000;
+    auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
+
+    // Create extra trace packet to trigger and finalize fence trace packets.
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    tracingSession->StopBlocking();
+
+    std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+    ASSERT_GT(raw_trace.size(), 0);
+
+    perfetto::protos::Trace trace;
+    ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+    ASSERT_FALSE(trace.packet().empty());
+    EXPECT_EQ(trace.packet().size(), 2);
+
+    const auto& packet1 = trace.packet().Get(0);
+    ASSERT_TRUE(packet1.has_timestamp());
+    EXPECT_EQ(packet1.timestamp(), signalTime1);
+    ASSERT_TRUE(packet1.has_graphics_frame_event());
+    ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
+    ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
+
+    const auto& packet2 = trace.packet().Get(1);
+    ASSERT_TRUE(packet2.has_timestamp());
+    EXPECT_EQ(packet2.timestamp(), signalTime2);
+    ASSERT_TRUE(packet2.has_graphics_frame_event());
+    ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event());
+    ASSERT_FALSE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
+}
+
+TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+    const nsecs_t signalTime = systemTime() - FrameTracer::kFenceSignallingDeadline;
+
+    auto tracingSession = getTracingSessionForTest();
+    auto fence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+    tracingSession->StartBlocking();
+    // Clean up irrelevant traces.
+    tracingSession->ReadTraceBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence, type);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime);
+    // Create extra trace packet to trigger and finalize any previous fence packets.
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    tracingSession->StopBlocking();
+
+    std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+    EXPECT_EQ(raw_trace.size(), 0);
+}
+
+TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
+    const std::string layerName = "co.layername#0";
+    const int32_t layerId = 5;
+    const uint32_t bufferID = 4;
+    const uint64_t frameNumber = 3;
+    const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE;
+    const nsecs_t duration = 1234;
+
+    auto tracingSession = getTracingSessionForTest();
+
+    tracingSession->StartBlocking();
+    // Clean up irrelevant traces.
+    tracingSession->ReadTraceBlocking();
+    mFrameTracer->traceNewLayer(layerId, layerName);
+
+    // traceFence called after fence signalled.
+    const nsecs_t signalTime1 = systemTime();
+    const nsecs_t startTime1 = signalTime1 - duration;
+    auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence1, type, startTime1);
+
+    // traceFence called before fence signalled.
+    const nsecs_t signalTime2 = systemTime();
+    const nsecs_t startTime2 = signalTime2 - duration;
+    auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+    mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence2, type, startTime2);
+    fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2);
+
+    // Create extra trace packet to trigger and finalize fence trace packets.
+    mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED);
+    tracingSession->StopBlocking();
+
+    std::vector<char> raw_trace = tracingSession->ReadTraceBlocking();
+    ASSERT_GT(raw_trace.size(), 0);
+
+    perfetto::protos::Trace trace;
+    ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size())));
+    ASSERT_FALSE(trace.packet().empty());
+    EXPECT_EQ(trace.packet().size(), 2);
+
+    const auto& packet1 = trace.packet().Get(0);
+    ASSERT_TRUE(packet1.has_timestamp());
+    EXPECT_EQ(packet1.timestamp(), startTime1);
+    ASSERT_TRUE(packet1.has_graphics_frame_event());
+    ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
+    ASSERT_TRUE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
+    const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event();
+    EXPECT_EQ(buffer_event1.duration_ns(), duration);
+
+    const auto& packet2 = trace.packet().Get(1);
+    ASSERT_TRUE(packet2.has_timestamp());
+    EXPECT_EQ(packet2.timestamp(), startTime2);
+    ASSERT_TRUE(packet2.has_graphics_frame_event());
+    ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event());
+    ASSERT_TRUE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
+    const auto& buffer_event2 = packet2.graphics_frame_event().buffer_event();
+    EXPECT_EQ(buffer_event2.duration_ns(), duration);
+}
+
+} // namespace
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
new file mode 100644
index 0000000..c6fe205
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <vector>
+
+#include <gmock/gmock.h>
+#include <gui/LayerMetadata.h>
+#include <log/log.h>
+
+#include "DisplayHardware/HWComposer.h"
+#include "mock/DisplayHardware/MockComposer.h"
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+
+namespace android {
+namespace {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::ElementsAreArray;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrictMock;
+
+struct MockHWC2ComposerCallback : public HWC2::ComposerCallback {
+    ~MockHWC2ComposerCallback() = default;
+
+    MOCK_METHOD3(onHotplugReceived,
+                 void(int32_t sequenceId, hwc2_display_t display, HWC2::Connection connection));
+    MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hwc2_display_t display));
+    MOCK_METHOD4(onVsyncReceived,
+                 void(int32_t sequenceId, hwc2_display_t display, int64_t timestamp,
+                      std::optional<hwc2_vsync_period_t> vsyncPeriod));
+    MOCK_METHOD3(onVsyncPeriodTimingChangedReceived,
+                 void(int32_t sequenceId, hwc2_display_t display,
+                      const hwc_vsync_period_change_timeline_t& updatedTimeline));
+    MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hwc2_display_t display));
+};
+
+struct HWComposerTest : public testing::Test {
+    Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+};
+
+struct HWComposerSetConfigurationTest : public HWComposerTest {
+    StrictMock<MockHWC2ComposerCallback> mCallback;
+};
+
+TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) {
+    const std::string kMetadata1Name = "com.example.metadata.1";
+    constexpr bool kMetadata1Mandatory = false;
+    const std::string kMetadata2Name = "com.example.metadata.2";
+    constexpr bool kMetadata2Mandatory = true;
+
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities())
+            .WillOnce(Return(std::vector<Hwc2::IComposer::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
+            .WillOnce(DoAll(SetArgPointee<0>(
+                                    std::vector<Hwc2::IComposerClient::LayerGenericMetadataKey>{
+                                            {kMetadata1Name, kMetadata1Mandatory},
+                                            {kMetadata2Name, kMetadata2Mandatory},
+                                    }),
+                            Return(hardware::graphics::composer::V2_4::Error::NONE)));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    EXPECT_EQ(2u, supported.size());
+    EXPECT_EQ(1u, supported.count(kMetadata1Name));
+    EXPECT_EQ(kMetadata1Mandatory, supported.find(kMetadata1Name)->second);
+    EXPECT_EQ(1u, supported.count(kMetadata2Name));
+    EXPECT_EQ(kMetadata2Mandatory, supported.find(kMetadata2Name)->second);
+}
+
+TEST_F(HWComposerSetConfigurationTest, handlesUnsupportedCallToGetLayerGenericMetadataKeys) {
+    EXPECT_CALL(*mHal, getMaxVirtualDisplayCount()).WillOnce(Return(0));
+    EXPECT_CALL(*mHal, getCapabilities())
+            .WillOnce(Return(std::vector<Hwc2::IComposer::Capability>{}));
+    EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+    EXPECT_CALL(*mHal, registerCallback(_));
+    EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+
+    impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+    hwc.setConfiguration(&mCallback, 123);
+
+    const auto& supported = hwc.getSupportedLayerGenericMetadata();
+    EXPECT_EQ(0u, supported.size());
+}
+
+struct HWComposerLayerTest : public testing::Test {
+    static constexpr hwc2_display_t kDisplayId = static_cast<hwc2_display_t>(1001);
+    static constexpr hwc2_layer_t kLayerId = static_cast<hwc2_layer_t>(1002);
+
+    HWComposerLayerTest(const std::unordered_set<HWC2::Capability>& capabilities)
+          : mCapabilies(capabilities) {}
+
+    ~HWComposerLayerTest() override { EXPECT_CALL(*mHal, destroyLayer(kDisplayId, kLayerId)); }
+
+    std::unique_ptr<Hwc2::mock::Composer> mHal{new StrictMock<Hwc2::mock::Composer>()};
+    const std::unordered_set<HWC2::Capability> mCapabilies;
+    HWC2::impl::Layer mLayer{*mHal, mCapabilies, kDisplayId, kLayerId};
+};
+
+struct HWComposerLayerGenericMetadataTest : public HWComposerLayerTest {
+    static const std::string kLayerGenericMetadata1Name;
+    static constexpr bool kLayerGenericMetadata1Mandatory = false;
+    static const std::vector<uint8_t> kLayerGenericMetadata1Value;
+    static const std::string kLayerGenericMetadata2Name;
+    static constexpr bool kLayerGenericMetadata2Mandatory = true;
+    static const std::vector<uint8_t> kLayerGenericMetadata2Value;
+
+    HWComposerLayerGenericMetadataTest() : HWComposerLayerTest({}) {}
+};
+
+const std::string HWComposerLayerGenericMetadataTest::kLayerGenericMetadata1Name =
+        "com.example.metadata.1";
+
+const std::vector<uint8_t> HWComposerLayerGenericMetadataTest::kLayerGenericMetadata1Value = {1u,
+                                                                                              2u,
+                                                                                              3u};
+
+const std::string HWComposerLayerGenericMetadataTest::kLayerGenericMetadata2Name =
+        "com.example.metadata.2";
+
+const std::vector<uint8_t> HWComposerLayerGenericMetadataTest::kLayerGenericMetadata2Value = {45u,
+                                                                                              67u};
+
+TEST_F(HWComposerLayerGenericMetadataTest, forwardsSupportedMetadata) {
+    EXPECT_CALL(*mHal,
+                setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata1Name,
+                                        kLayerGenericMetadata1Mandatory,
+                                        kLayerGenericMetadata1Value))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::NONE));
+    auto result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata1Name,
+                                                 kLayerGenericMetadata1Mandatory,
+                                                 kLayerGenericMetadata1Value);
+    EXPECT_EQ(HWC2::Error::None, result);
+
+    EXPECT_CALL(*mHal,
+                setLayerGenericMetadata(kDisplayId, kLayerId, kLayerGenericMetadata2Name,
+                                        kLayerGenericMetadata2Mandatory,
+                                        kLayerGenericMetadata2Value))
+            .WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
+    result = mLayer.setLayerGenericMetadata(kLayerGenericMetadata2Name,
+                                            kLayerGenericMetadata2Mandatory,
+                                            kLayerGenericMetadata2Value);
+    EXPECT_EQ(HWC2::Error::Unsupported, result);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 8e7440c..18e9941 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -1,140 +1,270 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
-#define LOG_TAG "LayerHistoryUnittests"
+#define LOG_TAG "LayerHistoryTest"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
 
-#include <mutex>
-#include <thread>
-
 #include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfo.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockLayer.h"
 
 using testing::_;
 using testing::Return;
 
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
 
 class LayerHistoryTest : public testing::Test {
-public:
-    LayerHistoryTest();
-    ~LayerHistoryTest() override;
-
 protected:
-    std::unique_ptr<LayerHistory> mLayerHistory;
+    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfo::PresentTimeHistory::HISTORY_SIZE;
+    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfo::MAX_FREQUENT_LAYER_PERIOD_NS;
 
-    static constexpr float MIN_REFRESH_RATE = 30.f;
-    static constexpr float MAX_REFRESH_RATE = 90.f;
-    static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u;
-    static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333;
+    static constexpr float LO_FPS = 30.f;
+    static constexpr nsecs_t LO_FPS_PERIOD = 33'333'333;
 
-    void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) {
-        mLayerHistory->setVisibility(testLayer, true);
-        for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
-            mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
-        }
-    };
+    static constexpr float HI_FPS = 90.f;
+    static constexpr nsecs_t HI_FPS_PERIOD = 11'111'111;
+
+    LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
+
+    impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
+    const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+
+    size_t layerCount() const { return mScheduler->layerHistorySize(); }
+    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+
+    size_t frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(), infos.begin() + history().mActiveLayersEnd,
+                             [now](const auto& pair) { return pair.second->isFrequent(now); });
+    }
+
+    auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+
+    RefreshRateConfigs mConfigs{{
+                                        RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
+                                                                        HwcConfigGroupType(0),
+                                                                        LO_FPS_PERIOD},
+                                        RefreshRateConfigs::InputConfig{HwcConfigIndexType(1),
+                                                                        HwcConfigGroupType(0),
+                                                                        HI_FPS_PERIOD},
+                                },
+                                HwcConfigIndexType(0)};
+    TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)};
+    TestableSurfaceFlinger mFlinger;
+
+    const nsecs_t mTime = systemTime();
 };
 
-LayerHistoryTest::LayerHistoryTest() {
-    mLayerHistory = std::make_unique<LayerHistory>();
-}
-LayerHistoryTest::~LayerHistoryTest() {}
-
 namespace {
+
 TEST_F(LayerHistoryTest, oneLayer) {
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer =
-            mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer, true);
-    for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    // no layers are returned if no layers are active.
+    ASSERT_TRUE(history().summarize(mTime).empty());
+    EXPECT_EQ(0, activeLayerCount());
+
+    // no layers are returned if active layers have insufficient history.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        history().record(layer.get(), 0, mTime);
+        ASSERT_TRUE(history().summarize(mTime).empty());
+        EXPECT_EQ(1, activeLayerCount());
     }
 
-    // Add a few more. This time we should get MAX refresh rate as the layer
-    // becomes relevant
-    static constexpr auto A_FEW = 10;
-    for (auto i = 0u; i < A_FEW; i++) {
-        EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    // High FPS is returned once enough history has been recorded.
+    for (int i = 0; i < 10; i++) {
+        history().record(layer.get(), 0, mTime);
+        ASSERT_EQ(1, history().summarize(mTime).size());
+        EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+        EXPECT_EQ(1, activeLayerCount());
     }
 }
 
-TEST_F(LayerHistoryTest, oneHDRLayer) {
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer =
-            mLayerHistory->createLayer("TestHDRLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer, true);
-
-    mLayerHistory->insert(testLayer, 0, true /*isHDR*/);
-    EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-    EXPECT_EQ(true, mLayerHistory->getDesiredRefreshRateAndHDR().second);
-
-    mLayerHistory->setVisibility(testLayer, false);
-    EXPECT_FLOAT_EQ(0.0f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-    EXPECT_EQ(false, mLayerHistory->getDesiredRefreshRateAndHDR().second);
-}
-
 TEST_F(LayerHistoryTest, explicitTimestamp) {
-    std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
-            mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(test30FpsLayer, true);
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
 
-    nsecs_t startTime = systemTime();
-    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
-                              false /*isHDR*/);
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = mTime;
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += LO_FPS_PERIOD;
     }
 
-    EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+    ASSERT_EQ(1, history().summarize(mTime).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(mTime)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
 }
 
 TEST_F(LayerHistoryTest, multipleLayers) {
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer =
-            mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer, true);
-    std::unique_ptr<LayerHistory::LayerHandle> test30FpsLayer =
-            mLayerHistory->createLayer("30FpsLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(test30FpsLayer, true);
-    std::unique_ptr<LayerHistory::LayerHandle> testLayer2 =
-            mLayerHistory->createLayer("TestLayer2", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
-    mLayerHistory->setVisibility(testLayer2, true);
+    auto layer1 = createLayer();
+    auto layer2 = createLayer();
+    auto layer3 = createLayer();
 
-    nsecs_t startTime = systemTime();
-    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+    EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
+    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+    nsecs_t time = mTime;
+
+    EXPECT_EQ(3, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer1 is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer1.get(), time, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
-    startTime = systemTime();
-    for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
-                              false /*isHDR*/);
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer2 is frequent and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time);
+        time += HI_FPS_PERIOD;
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
-    for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) {
-        mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
-                              false /*isHDR*/);
+    // layer1 is still active but infrequent.
+    history().record(layer1.get(), time, time);
+
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer1 is no longer active.
+    // layer2 is frequent and has low refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time);
+        time += LO_FPS_PERIOD;
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
 
-    // This frame is only around for 9 occurrences, so it doesn't throw
-    // anything off.
-    for (int i = 0; i < 9; i++) {
-        mLayerHistory->insert(testLayer2, 0, false /*isHDR*/);
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 has high refresh rate but not enough history.
+    constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        if (i % RATIO == 0) {
+            history().record(layer2.get(), time, time);
+        }
+
+        history().record(layer3.get(), time, time);
+        time += HI_FPS_PERIOD;
     }
-    EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-    // After 1200 ms frames become obsolete.
-    std::this_thread::sleep_for(std::chrono::milliseconds(1500));
 
-    mLayerHistory->insert(test30FpsLayer,
-                          startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL),
-                          false /*isHDR*/);
-    EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer3 becomes recently active.
+    history().record(layer3.get(), time, time);
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer1 expires.
+    layer1.clear();
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, layerCount());
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 becomes inactive.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 expires.
+    layer2.clear();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer3 becomes active and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer3.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer3 expires.
+    layer3.clear();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
 }
 
 } // namespace
-} // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
new file mode 100644
index 0000000..959c256
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "LayerHistoryTestV2"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+#include "Scheduler/LayerHistory.h"
+#include "Scheduler/LayerInfoV2.h"
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockLayer.h"
+
+using testing::_;
+using testing::Return;
+
+namespace android::scheduler {
+
+class LayerHistoryTestV2 : public testing::Test {
+protected:
+    static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
+    static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
+
+    static constexpr float LO_FPS = 30.f;
+    static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
+
+    static constexpr float HI_FPS = 90.f;
+    static constexpr auto HI_FPS_PERIOD = static_cast<nsecs_t>(1e9f / HI_FPS);
+
+    LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); }
+
+    impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); }
+    const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); }
+
+    size_t layerCount() const { return mScheduler->layerHistorySize(); }
+    size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+
+    auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
+        const auto& infos = history().mLayerInfos;
+        return std::count_if(infos.begin(),
+                             infos.begin() + static_cast<long>(history().mActiveLayersEnd),
+                             [now](const auto& pair) { return pair.second->isFrequent(now); });
+    }
+
+    void setLayerInfoVote(Layer* layer,
+                          LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
+        for (auto& [weak, info] : history().mLayerInfos) {
+            if (auto strong = weak.promote(); strong && strong.get() == layer) {
+                info->setDefaultLayerVote(vote);
+                info->setLayerVote(vote, 0);
+                return;
+            }
+        }
+    }
+
+    auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+
+    RefreshRateConfigs mConfigs{{
+                                        RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
+                                                                        HwcConfigGroupType(0),
+                                                                        LO_FPS_PERIOD},
+                                        RefreshRateConfigs::InputConfig{HwcConfigIndexType(1),
+                                                                        HwcConfigGroupType(0),
+                                                                        HI_FPS_PERIOD},
+                                },
+                                HwcConfigIndexType(0)};
+    TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)};
+    TestableSurfaceFlinger mFlinger;
+
+};
+
+namespace {
+
+TEST_F(LayerHistoryTestV2, oneLayer) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    const nsecs_t time = systemTime();
+
+    // No layers returned if no layers are active.
+    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+
+    // Max returned if active layers have insufficient history.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        history().record(layer.get(), 0, time);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+    }
+
+    // Max is returned since we have enough history but there is no timestamp votes.
+    for (int i = 0; i < 10; i++) {
+        history().record(layer.get(), 0, time);
+        ASSERT_EQ(1, history().summarize(time).size());
+        EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+        EXPECT_EQ(1, activeLayerCount());
+    }
+}
+
+TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+
+    history().record(layer.get(), 0, time);
+    auto summary = history().summarize(time);
+    ASSERT_EQ(1, history().summarize(time).size());
+    // Layer is still considered inactive so we expect to get Min
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
+
+    summary = history().summarize(time);
+    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+}
+
+TEST_F(LayerHistoryTestV2, explicitTimestamp) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
+    const auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
+    auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate())
+            .WillRepeatedly(
+                    Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
+    auto layer = createLayer();
+    EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer, getFrameRate())
+            .WillRepeatedly(Return(
+                    Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
+
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+
+    nsecs_t time = systemTime();
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitExactOrMultiple,
+              history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(73.4f, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer became inactive
+    setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+    time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+    ASSERT_TRUE(history().summarize(time).empty());
+    // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+TEST_F(LayerHistoryTestV2, multipleLayers) {
+    auto layer1 = createLayer();
+    auto layer2 = createLayer();
+    auto layer3 = createLayer();
+
+    EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
+    EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+
+    nsecs_t time = systemTime();
+
+    EXPECT_EQ(3, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer1 is active but infrequent.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer1.get(), time, time);
+        time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer2 is frequent and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    // layer1 is still active but infrequent.
+    history().record(layer1.get(), time, time);
+
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+    ASSERT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer1 is no longer active.
+    // layer2 is frequent and has low refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 has high refresh rate but not enough history.
+    constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
+        if (i % RATIO == 0) {
+            history().record(layer2.get(), time, time);
+        }
+
+        history().record(layer3.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[1].vote);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer3 becomes recently active.
+    history().record(layer3.get(), time, time);
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer1 expires.
+    layer1.clear();
+    ASSERT_EQ(2, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[1].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[1].desiredRefreshRate);
+    EXPECT_EQ(2, layerCount());
+    EXPECT_EQ(2, activeLayerCount());
+    EXPECT_EQ(2, frequentLayerCount(time));
+
+    // layer2 still has low refresh rate.
+    // layer3 becomes inactive.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer2.get(), time, time);
+        time += LO_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(LO_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer2 expires.
+    layer2.clear();
+    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+
+    // layer3 becomes active and has high refresh rate.
+    for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+        history().record(layer3.get(), time, time);
+        time += HI_FPS_PERIOD;
+    }
+
+    ASSERT_EQ(1, history().summarize(time).size());
+    EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, history().summarize(time)[0].vote);
+    EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time)[0].desiredRefreshRate);
+    EXPECT_EQ(1, layerCount());
+    EXPECT_EQ(1, activeLayerCount());
+    EXPECT_EQ(1, frequentLayerCount(time));
+
+    // layer3 expires.
+    layer3.clear();
+    EXPECT_TRUE(history().summarize(time).empty());
+    EXPECT_EQ(0, layerCount());
+    EXPECT_EQ(0, activeLayerCount());
+    EXPECT_EQ(0, frequentLayerCount(time));
+}
+
+} // namespace
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
similarity index 75%
rename from services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
rename to services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
index eff22b6..0208728 100644
--- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp
@@ -21,17 +21,17 @@
 #include <utils/Log.h>
 
 #include "AsyncCallRecorder.h"
-#include "Scheduler/IdleTimer.h"
+#include "Scheduler/OneShotTimer.h"
 
 using namespace std::chrono_literals;
 
 namespace android {
 namespace scheduler {
 
-class IdleTimerTest : public testing::Test {
+class OneShotTimerTest : public testing::Test {
 protected:
-    IdleTimerTest() = default;
-    ~IdleTimerTest() override = default;
+    OneShotTimerTest() = default;
+    ~OneShotTimerTest() override = default;
 
     // This timeout should be used when a 3ms callback is expected.
     // While the tests typically request a callback after 3ms, the scheduler
@@ -46,7 +46,7 @@
     AsyncCallRecorder<void (*)()> mResetTimerCallback;
     AsyncCallRecorder<void (*)()> mExpiredTimerCallback;
 
-    std::unique_ptr<IdleTimer> mIdleTimer;
+    std::unique_ptr<OneShotTimer> mIdleTimer;
 
     void clearPendingCallbacks() {
         while (mExpiredTimerCallback.waitForCall(0us).has_value()) {
@@ -55,13 +55,14 @@
 };
 
 namespace {
-TEST_F(IdleTimerTest, createAndDestroyTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {}, [] {});
+TEST_F(OneShotTimerTest, createAndDestroyTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(
+            3ms, [] {}, [] {});
 }
 
-TEST_F(IdleTimerTest, startStopTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, startStopTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(30ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     auto startTime = std::chrono::steady_clock::now();
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
@@ -70,7 +71,7 @@
     bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value();
     // Under ideal conditions there should be no event. But occasionally
     // it is possible that the wait just prior takes more than 30ms, and
-    // a callback is observed. We check the elapsed time since before the IdleTimer
+    // a callback is observed. We check the elapsed time since before the OneShotTimer
     // thread was started as a sanity check to not have a flakey test.
     EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms);
 
@@ -79,9 +80,9 @@
     mIdleTimer->stop();
 }
 
-TEST_F(IdleTimerTest, resetTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, resetTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     // Observe any event that happens in about 25ms. We don't care if one was
@@ -104,9 +105,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, resetBackToBackTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, resetBackToBackTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
@@ -135,9 +136,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, startNotCalledTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, startNotCalledTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     // The start hasn't happened, so the callback does not happen.
     EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
     EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
@@ -147,9 +148,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, idleTimerIdlesTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, idleTimerIdlesTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
@@ -167,18 +168,18 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value());
 }
 
-TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
     mIdleTimer->stop();
 }
 
-TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
     EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value());
@@ -190,9 +191,9 @@
     EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value());
 }
 
-TEST_F(IdleTimerTest, noCallbacksAfterStopTest) {
-    mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(),
-                                                        mExpiredTimerCallback.getInvocable());
+TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) {
+    mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(),
+                                                           mExpiredTimerCallback.getInvocable());
     mIdleTimer->start();
     EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
 
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
new file mode 100644
index 0000000..910e73b
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/PhaseOffsets.h"
+
+using namespace testing;
+
+namespace android {
+namespace scheduler {
+
+class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
+public:
+    TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+                                    nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+                                    nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+          : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
+                                 sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
+                                 appEarlyGlDuration) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+    PhaseOffsetsTest()
+          : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+                          38'000'000) {}
+
+    ~PhaseOffsetsTest() = default;
+
+    TestablePhaseOffsetsAsDurations mPhaseOffsets;
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
+    mPhaseOffsets.setRefreshRateFps(60.0f);
+    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+
+    EXPECT_EQ(currentOffsets, offsets);
+    EXPECT_EQ(offsets.late.sf, 6'166'667);
+
+    EXPECT_EQ(offsets.late.app, 2'333'334);
+
+    EXPECT_EQ(offsets.early.sf, 666'667);
+
+    EXPECT_EQ(offsets.early.app, 500'001);
+
+    EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+
+    EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
+    mPhaseOffsets.setRefreshRateFps(90.0f);
+    auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+    auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+
+    EXPECT_EQ(currentOffsets, offsets);
+    EXPECT_EQ(offsets.late.sf, 611'111);
+
+    EXPECT_EQ(offsets.late.app, 2'333'333);
+
+    EXPECT_EQ(offsets.early.sf, -4'888'889);
+
+    EXPECT_EQ(offsets.early.app, 6'055'555);
+
+    EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+
+    EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+    TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+    auto validateOffsets = [](auto& offsets) {
+        EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+        EXPECT_EQ(offsets.late.app, 1'000'000);
+
+        EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+        EXPECT_EQ(offsets.early.app, 1'000'000);
+
+        EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+        EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+    };
+
+    phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
+    auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+    auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+    EXPECT_EQ(currentOffsets, offsets);
+    validateOffsets(offsets);
+
+    phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
+    currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+    offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+    EXPECT_EQ(currentOffsets, offsets);
+    validateOffsets(offsets);
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 5067fe8..99c5f3d 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -23,7 +23,6 @@
 
 #include "DisplayHardware/HWC2.h"
 #include "Scheduler/RefreshRateConfigs.h"
-#include "mock/DisplayHardware/MockDisplay.h"
 
 using namespace std::chrono_literals;
 using testing::_;
@@ -31,28 +30,28 @@
 namespace android {
 namespace scheduler {
 
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
 using RefreshRate = RefreshRateConfigs::RefreshRate;
+using LayerVoteType = RefreshRateConfigs::LayerVoteType;
+using LayerRequirement = RefreshRateConfigs::LayerRequirement;
 
 class RefreshRateConfigsTest : public testing::Test {
 protected:
-    static constexpr int CONFIG_ID_60 = 0;
-    static constexpr hwc2_config_t HWC2_CONFIG_ID_60 = 0;
-    static constexpr int CONFIG_ID_90 = 1;
-    static constexpr hwc2_config_t HWC2_CONFIG_ID_90 = 1;
-    static constexpr int64_t VSYNC_60 = 16666667;
-    static constexpr int64_t VSYNC_90 = 11111111;
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_72 = HwcConfigIndexType(1);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(2);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_120 = HwcConfigIndexType(3);
+    static inline const HwcConfigIndexType HWC_CONFIG_ID_30 = HwcConfigIndexType(4);
+    static inline const HwcConfigGroupType HWC_GROUP_ID_0 = HwcConfigGroupType(0);
+    static inline const HwcConfigGroupType HWC_GROUP_ID_1 = HwcConfigGroupType(1);
+    static constexpr auto VSYNC_30 = static_cast<int64_t>(1e9f / 30);
+    static constexpr auto VSYNC_60 = static_cast<int64_t>(1e9f / 60);
+    static constexpr auto VSYNC_72 = static_cast<int64_t>(1e9f / 72);
+    static constexpr auto VSYNC_90 = static_cast<int64_t>(1e9f / 90);
+    static constexpr auto VSYNC_120 = static_cast<int64_t>(1e9f / 120);
+    static constexpr int64_t VSYNC_60_POINT_4 = 16666665;
 
     RefreshRateConfigsTest();
     ~RefreshRateConfigsTest();
-
-    void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
-        ASSERT_EQ(left.configId, right.configId);
-        ASSERT_EQ(left.name, right.name);
-        ASSERT_EQ(left.fps, right.fps);
-    }
-
-    RefreshRateConfigs mConfigs;
 };
 
 RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -71,102 +70,719 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) {
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    mConfigs.populate(displayConfigs);
-
-    // We always store a configuration for screen off.
-    const auto& rates = mConfigs.getRefreshRates();
-    ASSERT_EQ(1, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
-    ASSERT_NE(rates.end(), powerSavingRate);
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT));
-
-    RefreshRate expectedConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedConfig, *powerSavingRate->second);
-
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-
-    // Sanity check that getRefreshRate() does not modify the underlying configs.
-    ASSERT_EQ(1, mConfigs.getRefreshRates().size());
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 }
 
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) {
-    auto display = new Hwc2::mock::Display();
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    displayConfigs.push_back(config60.build());
-    mConfigs.populate(displayConfigs);
-
-    const auto& rates = mConfigs.getRefreshRates();
-    ASSERT_EQ(2, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
-    const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
-    ASSERT_NE(rates.end(), powerSavingRate);
-    ASSERT_NE(rates.end(), defaultRate);
-    ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE));
-
-    RefreshRate expectedPowerSavingConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
-    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedPowerSavingConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-
-    // Sanity check that getRefreshRate() does not modify the underlying configs.
-    ASSERT_EQ(2, mConfigs.getRefreshRates().size());
+TEST_F(RefreshRateConfigsTest, invalidPolicy) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+    ASSERT_LT(refreshRateConfigs->setPolicy(HwcConfigIndexType(10), 60, 60, nullptr), 0);
+    ASSERT_LT(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 20, 40, nullptr), 0);
 }
 
-TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) {
-    auto display = new Hwc2::mock::Display();
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs;
-    auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60);
-    config60.setVsyncPeriod(VSYNC_60);
-    displayConfigs.push_back(config60.build());
-    auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90);
-    config90.setVsyncPeriod(VSYNC_90);
-    displayConfigs.push_back(config90.build());
-    mConfigs.populate(displayConfigs);
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
 
-    const auto& rates = mConfigs.getRefreshRates();
-    ASSERT_EQ(3, rates.size());
-    const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING);
-    const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
-    const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
-    ASSERT_NE(rates.end(), powerSavingRate);
-    ASSERT_NE(rates.end(), defaultRate);
-    ASSERT_NE(rates.end(), performanceRate);
+    const auto minRate = refreshRateConfigs->getMinRefreshRate();
+    const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
 
-    RefreshRate expectedPowerSavingConfig =
-            RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID};
-    assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second);
-    RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60};
-    assertRatesEqual(expectedDefaultConfig, *defaultRate->second);
-    RefreshRate expectedPerformanceConfig =
-            RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90};
-    assertRatesEqual(expectedPerformanceConfig, *performanceRate->second);
+    RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    ASSERT_EQ(expectedDefaultConfig, minRate);
+    RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps",
+                                             90};
+    ASSERT_EQ(expectedPerformanceConfig, performanceRate);
 
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    assertRatesEqual(expectedPowerSavingConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT));
-    ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
-    assertRatesEqual(expectedPerformanceConfig,
-                     *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE));
+    const auto minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    ASSERT_EQ(minRateByPolicy, minRate);
+    ASSERT_EQ(performanceRateByPolicy, performanceRate);
 }
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentGroups) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_1, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    const auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
+    const auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    ASSERT_EQ(expectedDefaultConfig, minRate);
+    ASSERT_EQ(expectedDefaultConfig, minRate60);
+    ASSERT_EQ(expectedDefaultConfig, performanceRate60);
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+
+    const auto minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    const auto performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_1, "90fps",
+                                             90};
+    ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+    ASSERT_EQ(expectedPerformanceConfig, minRate90);
+    ASSERT_EQ(expectedPerformanceConfig, performanceRate90);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+    auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+    RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    ASSERT_EQ(expectedDefaultConfig, minRate);
+    RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps",
+                                             90};
+    ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+
+    auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+    auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+    ASSERT_EQ(expectedDefaultConfig, minRate60);
+    ASSERT_EQ(expectedDefaultConfig, performanceRate60);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getCurrentRefreshRate) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+    {
+        auto current = refreshRateConfigs->getCurrentRefreshRate();
+        EXPECT_EQ(current.configId, HWC_CONFIG_ID_60);
+    }
+
+    refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+    {
+        auto current = refreshRateConfigs->getCurrentRefreshRate();
+        EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
+    }
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+    {
+        auto current = refreshRateConfigs->getCurrentRefreshRate();
+        EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    const auto makeLayerRequirements = [](float refreshRate) -> std::vector<LayerRequirement> {
+        return {{"testLayer", LayerVoteType::Heuristic, refreshRate, 1.0f}};
+    };
+
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(90.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(60.0f)));
+    EXPECT_EQ(expected90Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(45.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(30.0f)));
+    EXPECT_EQ(expected60Config,
+              refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_60_90) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_60_72_90) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60_72_90_120) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+             {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+             {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+    RefreshRate expected120Config = {HWC_CONFIG_ID_120, VSYNC_120, HWC_GROUP_ID_0, "120fps", 120};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 48.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 48.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest,
+       twoDeviceConfigs_getRefreshRateForContentV2_30_60_90_120_DifferentTypes) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+             {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+             {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 72};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+    RefreshRate expected120Config = {HWC_CONFIG_ID_120, VSYNC_120, HWC_GROUP_ID_0, "120fps", 120};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitDefault;
+    EXPECT_EQ(expected120Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.desiredRefreshRate = 24.0f;
+    lr1.vote = LayerVoteType::ExplicitDefault;
+    lr2.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_30_60_72_90) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+             {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 70};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::Min;
+    EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 90.0f;
+    lr.vote = LayerVoteType::Heuristic;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 30.0f;
+    EXPECT_EQ(expected30Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected72Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_PriorityTest) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
+             {HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::Min;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 24.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::Max;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::Max;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 15.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 30.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 45.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_24FpsVideo) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = fps;
+        const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+        printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+        EXPECT_EQ(expected60Config, refreshRate);
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent_Explicit) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Explicit) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::Heuristic;
+    lr1.desiredRefreshRate = 90.0f;
+    lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr2.desiredRefreshRate = 60.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
+TEST_F(RefreshRateConfigsTest, testInPolicy) {
+    RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60_POINT_4, HWC_GROUP_ID_0,
+                                         "60fps", 60};
+    ASSERT_TRUE(expectedDefaultConfig.inPolicy(60.000004f, 60.000004f));
+    ASSERT_TRUE(expectedDefaultConfig.inPolicy(59.0f, 60.1f));
+    ASSERT_FALSE(expectedDefaultConfig.inPolicy(75.0f, 90.0f));
+    ASSERT_FALSE(expectedDefaultConfig.inPolicy(60.0011f, 90.0f));
+    ASSERT_FALSE(expectedDefaultConfig.inPolicy(50.0f, 59.998f));
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_75HzContent) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected30Config = {HWC_CONFIG_ID_30, VSYNC_30, HWC_GROUP_ID_0, "30fps", 30};
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+    auto& lr = layers[0];
+
+    lr.vote = LayerVoteType::ExplicitExactOrMultiple;
+    for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
+        lr.desiredRefreshRate = fps;
+        const auto& refreshRate = refreshRateConfigs->getRefreshRateForContentV2(layers);
+        printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
+        EXPECT_EQ(expected90Config, refreshRate);
+    }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Multiples) {
+    std::vector<RefreshRateConfigs::InputConfig> configs{
+            {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+             {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+    auto refreshRateConfigs =
+            std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+    RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+    RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+    auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+                                                LayerRequirement{.weight = 1.0f}};
+    auto& lr1 = layers[0];
+    auto& lr2 = layers[1];
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 60.0f;
+    lr2.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30.0f;
+    lr2.vote = LayerVoteType::Heuristic;
+    lr2.desiredRefreshRate = 90.0f;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+
+    lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+    lr1.desiredRefreshRate = 30.0f;
+    lr2.vote = LayerVoteType::Max;
+    EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers));
+}
+
 } // namespace
 } // namespace scheduler
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
new file mode 100644
index 0000000..18b1063
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+/**
+ * This class covers all the test that are related to refresh rate selection.
+ */
+class RefreshRateSelectionTest : public testing::Test {
+public:
+    RefreshRateSelectionTest();
+    ~RefreshRateSelectionTest() override;
+
+protected:
+    static constexpr int DEFAULT_DISPLAY_WIDTH = 1920;
+    static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024;
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+    static constexpr int32_t PRIORITY_UNSET = -1;
+
+    void setupScheduler();
+    void setupComposer(int virtualDisplayCount);
+    sp<BufferQueueLayer> createBufferQueueLayer();
+    sp<BufferStateLayer> createBufferStateLayer();
+    sp<EffectLayer> createEffectLayer();
+
+    void setParent(Layer* child, Layer* parent);
+    void commitTransaction(Layer* layer);
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+
+    sp<Client> mClient;
+    sp<Layer> mParent;
+    sp<Layer> mChild;
+    sp<Layer> mGrandChild;
+};
+
+RefreshRateSelectionTest::RefreshRateSelectionTest() {
+    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());
+
+    setupScheduler();
+    setupComposer(0);
+}
+
+RefreshRateSelectionTest::~RefreshRateSelectionTest() {
+    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());
+}
+
+sp<BufferQueueLayer> RefreshRateSelectionTest::createBufferQueueLayer() {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                           LAYER_FLAGS, LayerMetadata());
+    return new BufferQueueLayer(args);
+}
+
+sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+                           LAYER_FLAGS, LayerMetadata());
+    return new BufferStateLayer(args);
+}
+
+sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+                           LayerMetadata());
+    return new EffectLayer(args);
+}
+
+void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) {
+    child->setParent(parent);
+}
+
+void RefreshRateSelectionTest::commitTransaction(Layer* layer) {
+    layer->commitTransaction(layer->getCurrentState());
+}
+
+void RefreshRateSelectionTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                       ISurfaceComposer::eConfigChangedSuppress)));
+
+    auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+    EXPECT_CALL(*primaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*primaryDispSync, getPeriod())
+            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+    EXPECT_CALL(*primaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(primaryDispSync),
+                            std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+                            std::move(sfEventThread));
+}
+
+void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) {
+    mComposer = new Hwc2::mock::Composer();
+    EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+    mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+    Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(RefreshRateSelectionTest, testPriorityOnBufferQueueLayers) {
+    mParent = createBufferQueueLayer();
+    mChild = createBufferQueueLayer();
+    setParent(mChild.get(), mParent.get());
+    mGrandChild = createBufferQueueLayer();
+    setParent(mGrandChild.get(), mChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child has its own priority.
+    mGrandChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child inherits from his parent.
+    mChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Grandchild inherits from his grand parent.
+    mParent->setFrameRateSelectionPriority(1);
+    commitTransaction(mParent.get());
+    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) {
+    mParent = createBufferStateLayer();
+    mChild = createBufferStateLayer();
+    setParent(mChild.get(), mParent.get());
+    mGrandChild = createBufferStateLayer();
+    setParent(mGrandChild.get(), mChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child has its own priority.
+    mGrandChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child inherits from his parent.
+    mChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Grandchild inherits from his grand parent.
+    mParent->setFrameRateSelectionPriority(1);
+    commitTransaction(mParent.get());
+    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+TEST_F(RefreshRateSelectionTest, testPriorityOnEffectLayers) {
+    mParent = createEffectLayer();
+    mChild = createEffectLayer();
+    setParent(mChild.get(), mParent.get());
+    mGrandChild = createEffectLayer();
+    setParent(mGrandChild.get(), mChild.get());
+
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child has its own priority.
+    mGrandChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(PRIORITY_UNSET, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Child inherits from his parent.
+    mChild->setFrameRateSelectionPriority(1);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(PRIORITY_UNSET, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+
+    // Grandchild inherits from his grand parent.
+    mParent->setFrameRateSelectionPriority(1);
+    commitTransaction(mParent.get());
+    mChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mChild.get());
+    mGrandChild->setFrameRateSelectionPriority(PRIORITY_UNSET);
+    commitTransaction(mGrandChild.get());
+    ASSERT_EQ(1, mParent->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mChild->getFrameRateSelectionPriority());
+    ASSERT_EQ(1, mGrandChild->getFrameRateSelectionPriority());
+}
+
+} // namespace
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index 411ec61..18d6bd2 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -22,7 +26,6 @@
 #include <thread>
 
 #include "Scheduler/RefreshRateStats.h"
-#include "mock/DisplayHardware/MockDisplay.h"
 #include "mock/MockTimeStats.h"
 
 using namespace std::chrono_literals;
@@ -34,17 +37,27 @@
 
 class RefreshRateStatsTest : public testing::Test {
 protected:
-    static constexpr int CONFIG_ID_90 = 0;
-    static constexpr int CONFIG_ID_60 = 1;
+    static inline const auto CONFIG_ID_0 = HwcConfigIndexType(0);
+    static inline const auto CONFIG_ID_1 = HwcConfigIndexType(1);
+    static inline const auto CONFIG_GROUP_0 = HwcConfigGroupType(0);
     static constexpr int64_t VSYNC_90 = 11111111;
     static constexpr int64_t VSYNC_60 = 16666667;
 
     RefreshRateStatsTest();
     ~RefreshRateStatsTest();
 
+    void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
+        mRefreshRateConfigs =
+                std::make_unique<RefreshRateConfigs>(configs, /*currentConfig=*/CONFIG_ID_0);
+        mRefreshRateStats =
+                std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
+                                                   /*currentConfigId=*/CONFIG_ID_0,
+                                                   /*currentPowerMode=*/HWC_POWER_MODE_OFF);
+    }
+
     mock::TimeStats mTimeStats;
-    RefreshRateConfigs mRefreshRateConfigs;
-    RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats};
+    std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<RefreshRateStats> mRefreshRateStats;
 };
 
 RefreshRateStatsTest::RefreshRateStatsTest() {
@@ -63,63 +76,46 @@
 /* ------------------------------------------------------------------------
  * Test cases
  */
-TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) {
-    std::vector<std::shared_ptr<const HWC2::Display::Config>> configs;
-    mRefreshRateConfigs.populate(configs);
-
-    // There is one default config, so the refresh rates should have one item.
-    EXPECT_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());
-
-    mRefreshRateConfigs.populate(configs);
+    init({{{CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90}}});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
-    EXPECT_EQ(2, times.size());
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(1, times.size());
     EXPECT_NE(0u, times.count("ScreenOff"));
-    EXPECT_EQ(1u, times.count("90fps"));
-    EXPECT_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();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(0, times["90fps"]);
+    EXPECT_EQ(0u, times.count("90fps"));
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(1u, times.count("90fps"));
+    EXPECT_LT(0, times["90fps"]);
 
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    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.
     EXPECT_LT(screenOff, times["ScreenOff"]);
@@ -127,93 +123,75 @@
 }
 
 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());
-
-    mRefreshRateConfigs.populate(configs);
+    init({{{CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90}, {CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60}}});
 
     EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
     EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
 
-    std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes();
-    EXPECT_EQ(3, times.size());
+    std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes();
+    ASSERT_EQ(1, times.size());
     EXPECT_NE(0u, times.count("ScreenOff"));
-    EXPECT_EQ(1u, times.count("60fps"));
-    EXPECT_EQ(0, times["60fps"]);
-    EXPECT_EQ(1u, times.count("90fps"));
-    EXPECT_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();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(sixty, times["60fps"]);
-    EXPECT_EQ(ninety, times["90fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
-    EXPECT_EQ(sixty, times["60fps"]);
-    EXPECT_LT(ninety, times["90fps"]);
+    ASSERT_EQ(1u, times.count("90fps"));
+    EXPECT_LT(0, times["90fps"]);
 
     // When power mode is normal, time for configs updates.
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_1);
+    int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
-    EXPECT_LT(sixty, times["60fps"]);
+    ASSERT_EQ(1u, times.count("60fps"));
+    EXPECT_LT(0, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_90);
-    sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+    int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_LT(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    ninety = mRefreshRateStats.getTotalTimes()["90fps"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_1);
+    ninety = mRefreshRateStats->getTotalTimes()["90fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_EQ(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_LT(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);
-    sixty = mRefreshRateStats.getTotalTimes()["60fps"];
+    mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
+    mRefreshRateStats->setConfigMode(CONFIG_ID_0);
+    sixty = mRefreshRateStats->getTotalTimes()["60fps"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
 
-    mRefreshRateStats.setConfigMode(CONFIG_ID_60);
-    screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"];
+    mRefreshRateStats->setConfigMode(CONFIG_ID_1);
+    screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
     std::this_thread::sleep_for(std::chrono::milliseconds(2));
-    times = mRefreshRateStats.getTotalTimes();
+    times = mRefreshRateStats->getTotalTimes();
     EXPECT_LT(screenOff, times["ScreenOff"]);
     EXPECT_EQ(ninety, times["90fps"]);
     EXPECT_EQ(sixty, times["60fps"]);
@@ -221,3 +199,6 @@
 } // namespace
 } // namespace scheduler
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
index 160f041..f19e554 100644
--- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "RegionSamplingTest"
 
@@ -69,16 +73,16 @@
         n++;
         return pixel;
     });
+
     EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
-                testing::FloatNear(0.083f, 0.01f));
+                testing::FloatNear(0.16f, 0.01f));
 }
 
 TEST_F(RegionSamplingTest, bimodal_tiebreaker) {
     std::generate(buffer.begin(), buffer.end(),
                   [n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; });
-    // presently there's no tiebreaking strategy in place, accept either of the means
     EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area),
-                testing::AnyOf(testing::FloatEq(1.0), testing::FloatEq(0.0f)));
+                testing::FloatEq(0.5f));
 }
 
 TEST_F(RegionSamplingTest, bounds_checking) {
@@ -137,3 +141,6 @@
 }
 
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 740115e..5db11ec 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -1,16 +1,36 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #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 "Scheduler/RefreshRateConfigs.h"
+#include "TestableScheduler.h"
 #include "mock/MockEventThread.h"
 
 using testing::_;
@@ -34,37 +54,14 @@
         MOCK_METHOD0(requestNextVsync, void());
     };
 
-    scheduler::RefreshRateConfigs mRefreshRateConfigs;
-
-    /**
-     * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else
-     * the same.
-     */
-    class MockScheduler : public android::Scheduler {
-    public:
-        MockScheduler(const scheduler::RefreshRateConfigs& refreshRateConfigs,
-                      std::unique_ptr<EventThread> eventThread)
-              : Scheduler([](bool) {}, refreshRateConfigs), mEventThread(std::move(eventThread)) {}
-
-        std::unique_ptr<EventThread> makeEventThread(
-                const char* /* connectionName */, DispSync* /* dispSync */,
-                nsecs_t /* phaseOffsetNs */, nsecs_t /* offsetThresholdForNextVsync */,
-                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;
+    std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
+    std::unique_ptr<TestableScheduler> mScheduler;
+
+    Scheduler::ConnectionHandle mConnectionHandle;
     mock::EventThread* mEventThread;
-    std::unique_ptr<MockScheduler> mScheduler;
     sp<MockEventThreadConnection> mEventThreadConnection;
 };
 
@@ -73,9 +70,15 @@
             ::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>();
+    std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
+            {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
+    mRefreshRateConfigs = std::make_unique<
+            scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
+
+    mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false);
+
+    auto eventThread = std::make_unique<mock::EventThread>();
     mEventThread = eventThread.get();
-    mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread));
     EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
 
     mEventThreadConnection = new MockEventThreadConnection(mEventThread);
@@ -85,9 +88,8 @@
     EXPECT_CALL(*mEventThread, createEventConnection(_, _))
             .WillRepeatedly(Return(mEventThreadConnection));
 
-    mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16, ResyncCallback(),
-                                                     impl::EventThread::InterceptVSyncsCallback());
-    EXPECT_TRUE(mConnectionHandle != nullptr);
+    mConnectionHandle = mScheduler->createConnection(std::move(eventThread));
+    EXPECT_TRUE(mConnectionHandle);
 }
 
 SchedulerTest::~SchedulerTest() {
@@ -101,78 +103,48 @@
  * 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(),
-                                                             ISurfaceComposer::
-                                                                     eConfigChangedSuppress));
-    EXPECT_TRUE(returnedValue == nullptr);
-    EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr);
-    EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(nullptr, PHYSICAL_DISPLAY_ID, 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);
+    Scheduler::ConnectionHandle handle;
 
-    sp<IDisplayEventConnection> returnedValue;
+    sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            returnedValue =
-                    mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback(),
-                                                             ISurfaceComposer::
-                                                                     eConfigChangedSuppress));
-    EXPECT_TRUE(returnedValue == nullptr);
-    EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr);
-    EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr);
+            connection = mScheduler->createDisplayEventConnection(handle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
+    EXPECT_FALSE(connection);
+    EXPECT_FALSE(mScheduler->getEventConnection(handle));
 
     // 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, PHYSICAL_DISPLAY_ID, false));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle));
 
     EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(connectionHandle));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle));
 
-    std::string testString;
+    std::string output;
     EXPECT_CALL(*mEventThread, dump(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(connectionHandle, testString));
-    EXPECT_TRUE(testString == "");
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output));
+    EXPECT_TRUE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0);
-    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(connectionHandle, 10));
+    ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10));
 }
 
 TEST_F(SchedulerTest, validConnectionHandle) {
-    sp<IDisplayEventConnection> returnedValue;
+    sp<IDisplayEventConnection> connection;
     ASSERT_NO_FATAL_FAILURE(
-            returnedValue =
-                    mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback(),
-                                                             ISurfaceComposer::
-                                                                     eConfigChangedSuppress));
-    EXPECT_TRUE(returnedValue != nullptr);
-    ASSERT_EQ(returnedValue, mEventThreadConnection);
-
-    EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr);
-    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr);
+            connection = mScheduler->createDisplayEventConnection(mConnectionHandle,
+                                                                  ISurfaceComposer::
+                                                                          eConfigChangedSuppress));
+    ASSERT_EQ(mEventThreadConnection, connection);
+    EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle));
 
     EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1);
     ASSERT_NO_FATAL_FAILURE(
-            mScheduler->hotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
+            mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false));
 
     EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle));
@@ -180,13 +152,22 @@
     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 != "");
+    std::string output("dump");
+    EXPECT_CALL(*mEventThread, dump(output)).Times(1);
+    ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output));
+    EXPECT_FALSE(output.empty());
 
     EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1);
     ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10));
+
+    static constexpr size_t kEventConnections = 5;
+    ON_CALL(*mEventThread, getEventThreadConnectionCount())
+            .WillByDefault(Return(kEventConnections));
+    EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle));
 }
+
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
index 5865579..5f6a715 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerUtilsTest.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "SchedulerUnittests"
 
@@ -128,4 +132,6 @@
 
 } // namespace
 } // namespace scheduler
-} // namespace android
\ No newline at end of file
+} // namespace android
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
new file mode 100644
index 0000000..5406879
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include "Scheduler/StrongTyping.h"
+
+using namespace testing;
+
+namespace android {
+
+TEST(StrongTypeTest, comparison) {
+    using SpunkyType = StrongTyping<int, struct SpunkyTypeTag, Compare>;
+    SpunkyType f2(22);
+    SpunkyType f1(10);
+
+    EXPECT_TRUE(f1 == f1);
+    EXPECT_TRUE(SpunkyType(10) != SpunkyType(11));
+    EXPECT_FALSE(SpunkyType(31) != SpunkyType(31));
+
+    EXPECT_TRUE(SpunkyType(10) < SpunkyType(11));
+    EXPECT_TRUE(SpunkyType(-1) < SpunkyType(0));
+    EXPECT_FALSE(SpunkyType(-10) < SpunkyType(-20));
+
+    EXPECT_TRUE(SpunkyType(10) <= SpunkyType(11));
+    EXPECT_TRUE(SpunkyType(10) <= SpunkyType(10));
+    EXPECT_TRUE(SpunkyType(-10) <= SpunkyType(1));
+    EXPECT_FALSE(SpunkyType(10) <= SpunkyType(9));
+
+    EXPECT_TRUE(SpunkyType(11) >= SpunkyType(11));
+    EXPECT_TRUE(SpunkyType(12) >= SpunkyType(11));
+    EXPECT_FALSE(SpunkyType(11) >= SpunkyType(12));
+
+    EXPECT_FALSE(SpunkyType(11) > SpunkyType(12));
+    EXPECT_TRUE(SpunkyType(-11) < SpunkyType(7));
+}
+
+TEST(StrongTypeTest, addition) {
+    using FunkyType = StrongTyping<int, struct FunkyTypeTag, Compare, Add>;
+    FunkyType f2(22);
+    FunkyType f1(10);
+
+    EXPECT_THAT(f1 + f2, Eq(FunkyType(32)));
+    EXPECT_THAT(f2 + f1, Eq(FunkyType(32)));
+
+    EXPECT_THAT(++f1.value(), Eq(11));
+    EXPECT_THAT(f1.value(), Eq(11));
+    EXPECT_THAT(f1++.value(), Eq(11));
+    EXPECT_THAT(f1++.value(), Eq(12));
+    EXPECT_THAT(f1.value(), Eq(13));
+
+    auto f3 = f1;
+    EXPECT_THAT(f1, Eq(f3));
+    EXPECT_THAT(f1, Lt(f2));
+
+    f3 += f1;
+    EXPECT_THAT(f1.value(), Eq(13));
+    EXPECT_THAT(f3.value(), Eq(26));
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index cb6980e..52da34b 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -19,30 +19,49 @@
 #include <gmock/gmock.h>
 #include <gui/ISurfaceComposer.h>
 
+#include "Scheduler/DispSync.h"
 #include "Scheduler/EventThread.h"
-#include "Scheduler/RefreshRateConfigs.h"
+#include "Scheduler/LayerHistory.h"
 #include "Scheduler/Scheduler.h"
 
 namespace android {
 
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ISchedulerCallback {
 public:
-    TestableScheduler(const scheduler::RefreshRateConfigs& refreshRateConfig)
-          : Scheduler([](bool) {}, refreshRateConfig) {}
+    TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+          : Scheduler([](bool) {}, configs, *this, useContentDetectionV2) {
+        if (mUseContentDetectionV2) {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+        } else {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+        }
+    }
 
-    // Creates EventThreadConnection with the given eventThread. Creates Scheduler::Connection
-    // and adds it to the list of connectins. Returns the ConnectionHandle for the
-    // Scheduler::Connection. This allows plugging in mock::EventThread.
-    sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) {
-        sp<EventThreadConnection> eventThreadConnection =
-                new EventThreadConnection(eventThread.get(), ResyncCallback(),
-                                          ISurfaceComposer::eConfigChangedSuppress);
-        const int64_t id = sNextId++;
-        mConnections.emplace(id,
-                             std::make_unique<Scheduler::Connection>(new ConnectionHandle(id),
-                                                                     eventThreadConnection,
-                                                                     std::move(eventThread)));
-        return mConnections[id]->handle;
+    TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
+                      std::unique_ptr<EventControlThread> eventControlThread,
+                      const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
+          : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
+                      useContentDetectionV2) {
+        if (mUseContentDetectionV2) {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
+        } else {
+            mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>();
+        }
+    }
+
+    // Used to inject mock event thread.
+    ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
+        return Scheduler::createConnection(std::move(eventThread));
+    }
+
+    size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS {
+        if (mUseContentDetectionV2) {
+            return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get())
+                    ->mLayerInfos.size();
+        } else {
+            return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get())
+                    ->mLayerInfos.size();
+        }
     }
 
     /* ------------------------------------------------------------------------
@@ -53,6 +72,12 @@
     auto& mutableEventControlThread() { return mEventControlThread; }
     auto& mutablePrimaryDispSync() { return mPrimaryDispSync; }
     auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
+    auto mutableLayerHistory() {
+        return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get());
+    }
+    auto mutableLayerHistoryV2() {
+        return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get());
+    }
 
     ~TestableScheduler() {
         // All these pointer and container clears help ensure that GMock does
@@ -62,7 +87,11 @@
         mutableEventControlThread().reset();
         mutablePrimaryDispSync().reset();
         mConnections.clear();
-    };
+    }
+
+private:
+    void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+    void repaintEverythingForHWC() override {}
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 64d34ee..3a4f349 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -17,27 +17,27 @@
 #pragma once
 
 #include <compositionengine/Display.h>
-#include <compositionengine/Layer.h>
+#include <compositionengine/LayerFECompositionState.h>
 #include <compositionengine/OutputLayer.h>
 #include <compositionengine/impl/CompositionEngine.h>
-#include <compositionengine/impl/LayerCompositionState.h>
 #include <compositionengine/impl/OutputLayerCompositionState.h>
+#include <compositionengine/mock/DisplaySurface.h>
 
 #include "BufferQueueLayer.h"
 #include "BufferStateLayer.h"
-#include "ColorLayer.h"
 #include "ContainerLayer.h"
 #include "DisplayDevice.h"
+#include "EffectLayer.h"
 #include "FakePhaseOffsets.h"
 #include "Layer.h"
 #include "NativeWindowSurface.h"
 #include "Scheduler/MessageQueue.h"
+#include "Scheduler/RefreshRateConfigs.h"
 #include "StartPropertySetThread.h"
 #include "SurfaceFlinger.h"
-#include "SurfaceFlingerFactory.h"
+#include "SurfaceFlingerDefaultFactory.h"
 #include "SurfaceInterceptor.h"
-
-#include "TimeStats/TimeStats.h"
+#include "TestableScheduler.h"
 
 namespace android {
 
@@ -61,66 +61,74 @@
 public:
     ~Factory() = default;
 
-    std::unique_ptr<DispSync> createDispSync(const char*, bool, int64_t) override {
-        // TODO: Use test-fixture controlled factory
+    std::unique_ptr<DispSync> createDispSync(const char*, bool) override {
         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 {
+    std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+            const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
         return std::make_unique<scheduler::FakePhaseOffsets>();
     }
 
     std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
-                                               const scheduler::RefreshRateConfigs&) override {
-        // TODO: Use test-fixture controlled factory
+                                               const scheduler::RefreshRateConfigs&,
+                                               ISchedulerCallback&) override {
         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;
+        if (!mCreateBufferQueue) {
+            BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
+            return;
+        }
         mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger);
     }
 
+    sp<IGraphicBufferProducer> createMonitoredProducer(const sp<IGraphicBufferProducer>& producer,
+                                                       const sp<SurfaceFlinger>& flinger,
+                                                       const wp<Layer>& layer) override {
+        return new MonitoredProducer(producer, flinger, layer);
+    }
+
+    sp<BufferLayerConsumer> createBufferLayerConsumer(const sp<IGraphicBufferConsumer>& consumer,
+                                                      renderengine::RenderEngine& renderEngine,
+                                                      uint32_t textureName, Layer* layer) override {
+        return new BufferLayerConsumer(consumer, renderEngine, textureName, layer);
+    }
+
     std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface(
             const sp<IGraphicBufferProducer>& producer) override {
         if (!mCreateNativeWindowSurface) return nullptr;
@@ -132,30 +140,19 @@
     }
 
     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<EffectLayer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
 
     sp<ContainerLayer> createContainerLayer(const LayerCreationArgs&) override {
-        // TODO: Use test-fixture controlled factory
         return nullptr;
     }
 
-    std::shared_ptr<TimeStats> createTimeStats() override {
-        // TODO: Use test-fixture controlled factory
-        return std::make_shared<android::impl::TimeStats>();
-    }
-
     using CreateBufferQueueFunction =
             std::function<void(sp<IGraphicBufferProducer>* /* outProducer */,
                                sp<IGraphicBufferConsumer>* /* outConsumer */,
@@ -176,6 +173,9 @@
 
 class TestableSurfaceFlinger {
 public:
+    SurfaceFlinger* flinger() { return mFlinger.get(); }
+    TestableScheduler* scheduler() { return mScheduler; }
+
     // Extend this as needed for accessing SurfaceFlinger private (and public)
     // functions.
 
@@ -188,6 +188,41 @@
                 std::make_unique<impl::HWComposer>(std::move(composer)));
     }
 
+    void setupTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+        mFlinger->mCompositionEngine->setTimeStats(timeStats);
+    }
+
+    void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
+                        std::unique_ptr<EventControlThread> eventControlThread,
+                        std::unique_ptr<EventThread> appEventThread,
+                        std::unique_ptr<EventThread> sfEventThread,
+                        bool useContentDetectionV2 = false) {
+        std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
+                {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
+        mFlinger->mRefreshRateConfigs = std::make_unique<
+                scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0));
+        mFlinger->mRefreshRateStats = std::make_unique<
+                scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
+                                             /*currentConfig=*/HwcConfigIndexType(0),
+                                             /*powerMode=*/HWC_POWER_MODE_OFF);
+        mFlinger->mPhaseConfiguration =
+                mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
+
+        mScheduler =
+                new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
+                                      *mFlinger->mRefreshRateConfigs, useContentDetectionV2);
+
+        mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
+        mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
+        resetScheduler(mScheduler);
+
+        mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
+                                          mFlinger->mSfConnectionHandle,
+                                          mFlinger->mPhaseConfiguration->getCurrentOffsets());
+    }
+
+    void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+
     using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
     void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
         mFactory.mCreateBufferQueue = f;
@@ -208,10 +243,12 @@
     auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; }
     auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; }
 
+    auto& mutableStateLock() { return mFlinger->mStateLock; }
+
     void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
-        layer->getCompositionLayer()->editState().frontEnd.sidebandStream = sidebandStream;
+        layer->editCompositionState()->sidebandStream = sidebandStream;
     }
 
     void setLayerCompositionType(sp<Layer> layer, HWC2::Composition type) {
@@ -296,6 +333,22 @@
         return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
     }
 
+    auto& getTransactionQueue() { return mFlinger->mTransactionQueues; }
+
+    auto setTransactionState(const Vector<ComposerState>& states,
+                             const Vector<DisplayState>& displays, uint32_t flags,
+                             const sp<IBinder>& applyToken,
+                             const InputWindowCommands& inputWindowCommands,
+                             int64_t desiredPresentTime, const client_cache_t& uncacheBuffer,
+                             bool hasListenerCallbacks,
+                             std::vector<ListenerCallbacks>& listenerCallbacks) {
+        return mFlinger->setTransactionState(states, displays, flags, applyToken,
+                                             inputWindowCommands, desiredPresentTime, uncacheBuffer,
+                                             hasListenerCallbacks, listenerCallbacks);
+    }
+
+    auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+
     /* ------------------------------------------------------------------------
      * Read-only access to private data to assert post-conditions.
      */
@@ -315,7 +368,6 @@
      */
 
     auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
-    auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; }
     auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; }
 
     auto& mutableCurrentState() { return mFlinger->mCurrentState; }
@@ -332,16 +384,18 @@
     auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
     auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; }
     auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; }
+    auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
 
     auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; }
     auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
     auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
     auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
     auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
-    auto& mutableScheduler() { return mFlinger->mScheduler; }
-    auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; }
-    auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; }
-    auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; }
+
+    auto fromHandle(const sp<IBinder>& handle) {
+        Mutex::Autolock _l(mFlinger->mStateLock);
+        return mFlinger->fromHandle(handle);
+    }
 
     ~TestableSurfaceFlinger() {
         // All these pointer and container clears help ensure that GMock does
@@ -353,7 +407,7 @@
         mutableDrawingState().displays.clear();
         mutableEventQueue().reset();
         mutableInterceptor().reset();
-        mutableScheduler().reset();
+        mFlinger->mScheduler.reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
         mFlinger->mCompositionEngine->setRenderEngine(
                 std::unique_ptr<renderengine::RenderEngine>());
@@ -384,8 +438,9 @@
         static constexpr int32_t DEFAULT_WIDTH = 1920;
         static constexpr int32_t DEFAULT_HEIGHT = 1280;
         static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
+        static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
         static constexpr int32_t DEFAULT_DPI = 320;
-        static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
+        static constexpr hwc2_config_t DEFAULT_ACTIVE_CONFIG = 0;
         static constexpr int32_t DEFAULT_POWER_MODE = 2;
 
         FakeHwcDisplayInjector(DisplayId displayId, HWC2::DisplayType hwcDisplayType,
@@ -422,7 +477,7 @@
             return *this;
         }
 
-        auto& setActiveConfig(int32_t config) {
+        auto& setActiveConfig(hwc2_config_t config) {
             mActiveConfig = config;
             return *this;
         }
@@ -454,19 +509,18 @@
             config.setVsyncPeriod(mRefreshRate);
             config.setDpiX(mDpiX);
             config.setDpiY(mDpiY);
-            display->mutableConfigs().emplace(mActiveConfig, config.build());
+            config.setConfigGroup(mConfigGroup);
+            display->mutableConfigs().emplace(static_cast<int32_t>(mActiveConfig), config.build());
             display->mutableIsConnected() = true;
             display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
 
-            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get();
+            flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
 
             if (mHwcDisplayType == HWC2::DisplayType::Physical) {
                 flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
                 (mIsPrimary ? flinger->mutableInternalHwcDisplayId()
                             : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
             }
-
-            flinger->mFakeHwcDisplays.push_back(std::move(display));
         }
 
     private:
@@ -479,8 +533,9 @@
         int32_t mHeight = DEFAULT_HEIGHT;
         int32_t mRefreshRate = DEFAULT_REFRESH_RATE;
         int32_t mDpiX = DEFAULT_DPI;
+        int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
         int32_t mDpiY = DEFAULT_DPI;
-        int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
+        hwc2_config_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
         int32_t mPowerMode = DEFAULT_POWER_MODE;
         const std::unordered_set<HWC2::Capability>* mCapabilities = nullptr;
     };
@@ -488,10 +543,11 @@
     class FakeDisplayDeviceInjector {
     public:
         FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
-                                  const std::optional<DisplayId>& displayId, bool isVirtual,
+                                  std::optional<DisplayId> displayId,
+                                  std::optional<DisplayConnectionType> connectionType,
                                   bool isPrimary)
               : mFlinger(flinger), mCreationArgs(flinger.mFlinger.get(), mDisplayToken, displayId) {
-            mCreationArgs.isVirtual = isVirtual;
+            mCreationArgs.connectionType = connectionType;
             mCreationArgs.isPrimary = isPrimary;
         }
 
@@ -547,9 +603,19 @@
             return *this;
         }
 
+        auto& setPhysicalOrientation(ui::Rotation orientation) {
+            mCreationArgs.physicalOrientation = orientation;
+            return *this;
+        }
+
         sp<DisplayDevice> inject() {
             DisplayDeviceState state;
-            state.displayId = mCreationArgs.isVirtual ? std::nullopt : mCreationArgs.displayId;
+            if (const auto type = mCreationArgs.connectionType) {
+                const auto id = mCreationArgs.displayId;
+                LOG_ALWAYS_FATAL_IF(!id);
+                state.physical = {*id, *type};
+            }
+
             state.isSecure = mCreationArgs.isSecure;
 
             sp<DisplayDevice> device = new DisplayDevice(std::move(mCreationArgs));
@@ -557,9 +623,8 @@
             mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
             mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
 
-            if (!mCreationArgs.isVirtual) {
-                LOG_ALWAYS_FATAL_IF(!state.displayId);
-                mFlinger.mutablePhysicalDisplayTokens()[*state.displayId] = mDisplayToken;
+            if (const auto& physical = state.physical) {
+                mFlinger.mutablePhysicalDisplayTokens()[physical->id] = mDisplayToken;
             }
 
             return device;
@@ -573,9 +638,7 @@
 
     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;
+    TestableScheduler* mScheduler = nullptr;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index f35758d..a7a4d48 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -14,31 +14,41 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #undef LOG_TAG
 #define LOG_TAG "LibSurfaceFlingerUnittests"
 
+#include <TimeStats/TimeStats.h>
+#include <android/util/ProtoOutputStream.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
 #include <log/log.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
+#include <chrono>
 #include <random>
 #include <unordered_set>
 
-#include "TimeStats/TimeStats.h"
-
 #include "libsurfaceflinger_unittest_main.h"
 
 using namespace android::surfaceflinger;
 using namespace google::protobuf;
+using namespace std::chrono_literals;
 
 namespace android {
 namespace {
 
+using testing::_;
+using testing::AnyNumber;
 using testing::Contains;
+using testing::HasSubstr;
+using testing::InSequence;
 using testing::SizeIs;
+using testing::StrEq;
 using testing::UnorderedElementsAre;
 
 // clang-format off
@@ -133,7 +143,44 @@
     }
 
     std::mt19937 mRandomEngine = std::mt19937(std::random_device()());
-    std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>();
+
+    class FakeStatsEventDelegate : public impl::TimeStats::StatsEventDelegate {
+    public:
+        FakeStatsEventDelegate() = default;
+        ~FakeStatsEventDelegate() override = default;
+
+        struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
+            return mEvent;
+        }
+        void registerStatsPullAtomCallback(int32_t atom_tag,
+                                           AStatsManager_PullAtomCallback callback,
+                                           AStatsManager_PullAtomMetadata*, void* cookie) override {
+            mAtomTags.push_back(atom_tag);
+            mCallback = callback;
+            mCookie = cookie;
+        }
+
+        AStatsManager_PullAtomCallbackReturn makePullAtomCallback(int32_t atom_tag, void* cookie) {
+            return (*mCallback)(atom_tag, nullptr, cookie);
+        }
+
+        MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
+        MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
+        MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
+        MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
+        MOCK_METHOD2(statsEventWriteString8, void(AStatsEvent*, const char*));
+        MOCK_METHOD3(statsEventWriteByteArray, void(AStatsEvent*, const uint8_t*, size_t));
+        MOCK_METHOD1(statsEventBuild, void(AStatsEvent*));
+
+        AStatsEvent* mEvent = AStatsEvent_obtain();
+        std::vector<int32_t> mAtomTags;
+        AStatsManager_PullAtomCallback mCallback = nullptr;
+        void* mCookie = nullptr;
+    };
+    FakeStatsEventDelegate* mDelegate = new FakeStatsEventDelegate;
+    std::unique_ptr<TimeStats> mTimeStats =
+            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+                                              std::nullopt, std::nullopt);
 };
 
 std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) {
@@ -171,8 +218,8 @@
     return result;
 }
 
-static std::string genLayerName(int32_t layerID) {
-    return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID);
+static std::string genLayerName(int32_t layerId) {
+    return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerId);
 }
 
 void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) {
@@ -210,6 +257,25 @@
     return distr(mRandomEngine);
 }
 
+TEST_F(TimeStatsTest, disabledByDefault) {
+    ASSERT_FALSE(mTimeStats->isEnabled());
+}
+
+TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
+    mTimeStats->onBootFinished();
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+}
+
+TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+    EXPECT_CALL(*mDelegate,
+                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+    EXPECT_CALL(*mDelegate,
+                unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    mTimeStats.reset();
+}
+
 TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
     ASSERT_TRUE(mTimeStats->isEnabled());
@@ -246,6 +312,95 @@
     EXPECT_EQ(CLIENT_COMPOSITION_FRAMES, globalProto.client_composition_frames());
 }
 
+TEST_F(TimeStatsTest, canIncreaseLateAcquireFrames) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+        mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult = "lateAcquireFrames = " + std::to_string(LATE_ACQUIRE_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseBadDesiredPresent) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+        mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE_2, LAYER_ID_0, 2, 2000000);
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult =
+            "badDesiredPresentFrames = " + std::to_string(BAD_DESIRED_PRESENT_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) {
+    // this stat is not in the proto so verify by checking the string dump
+    constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2;
+
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    for (size_t i = 0; i < CLIENT_COMPOSITION_REUSED_FRAMES; i++) {
+        ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames());
+    }
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    const std::string expectedResult =
+            "clientCompositionReusedFrames = " + std::to_string(CLIENT_COMPOSITION_REUSED_FRAMES);
+    EXPECT_THAT(result, HasSubstr(expectedResult));
+}
+
+TEST_F(TimeStatsTest, canAverageFrameDuration) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                          .count());
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(16ms)
+                                          .count());
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageFrameDuration = 10.000 ms"));
+}
+
+TEST_F(TimeStatsTest, canAverageRenderEngineTimings) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+                                                   .count(),
+                                           std::make_shared<FenceTime>(
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(3ms)
+                                                           .count()));
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(8ms)
+                                                   .count());
+
+    // Push a dummy present fence to trigger flushing the RenderEngine timings.
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 3.000 ms"));
+}
+
 TEST_F(TimeStatsTest, canInsertGlobalPresentToPresent) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -275,6 +430,65 @@
     EXPECT_EQ(2, histogramProto.time_millis());
 }
 
+TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->setPowerMode(HWC_POWER_MODE_OFF);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+                                          .count());
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                          .count());
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, globalProto.frame_duration_size());
+    const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.frame_duration().Get(0);
+    EXPECT_EQ(1, histogramProto.frame_count());
+    EXPECT_EQ(3, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+                                                   .count(),
+                                           std::make_shared<FenceTime>(
+                                                   std::chrono::duration_cast<
+                                                           std::chrono::nanoseconds>(3ms)
+                                                           .count()));
+
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
+
+    // First verify that flushing RenderEngine durations did not occur yet.
+    SFTimeStatsGlobalProto preFlushProto;
+    ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+    ASSERT_EQ(0, preFlushProto.render_engine_timing_size());
+
+    // Push a dummy present fence to trigger flushing the RenderEngine timings.
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+    // Now we can verify that RenderEngine durations were flushed now.
+    SFTimeStatsGlobalProto postFlushProto;
+    ASSERT_TRUE(postFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    ASSERT_EQ(1, postFlushProto.render_engine_timing_size());
+    const SFTimeStatsHistogramBucketProto& histogramProto =
+            postFlushProto.render_engine_timing().Get(0);
+    EXPECT_EQ(2, histogramProto.frame_count());
+    EXPECT_EQ(2, histogramProto.time_millis());
+}
+
 TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -496,6 +710,15 @@
     ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementMissedFrames());
     ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames());
     ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL));
+
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                          .count());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
     ASSERT_NO_FATAL_FAILURE(
             mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000)));
     ASSERT_NO_FATAL_FAILURE(
@@ -512,9 +735,34 @@
     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.frame_duration_size());
+    EXPECT_EQ(0, globalProto.render_engine_timing_size());
     EXPECT_EQ(0, globalProto.stats_size());
 }
 
+TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) {
+    // These stats are not in the proto so verify by checking the string dump.
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+    ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionReusedFrames());
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats
+            ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+                                  std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+                                          .count());
+    mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+                                                   .count(),
+                                           std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+                                                   .count());
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+    EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty());
+
+    const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING));
+    EXPECT_THAT(result, HasSubstr("clientCompositionReusedFrames = 0"));
+    EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms"));
+    EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms"));
+}
+
 TEST_F(TimeStatsTest, canDumpWithMaxLayers) {
     EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
 
@@ -549,6 +797,351 @@
     ASSERT_EQ(0, globalProto.stats_size());
 }
 
+namespace {
+std::string buildExpectedHistogramBytestring(const std::vector<int32_t>& times,
+                                             const std::vector<int32_t>& frameCounts) {
+    util::ProtoOutputStream proto;
+    for (int i = 0; i < times.size(); i++) {
+        ALOGE("Writing time: %d", times[i]);
+        proto.write(util::FIELD_TYPE_INT32 | util::FIELD_COUNT_REPEATED | 1 /* field id */,
+                    (int32_t)times[i]);
+        ALOGE("Writing count: %d", frameCounts[i]);
+        proto.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED | 2 /* field id */,
+                    (int64_t)frameCounts[i]);
+    }
+    std::string byteString;
+    proto.serializeToString(&byteString);
+    return byteString;
+}
+
+std::string dumpByteStringHex(const std::string& str) {
+    std::stringstream ss;
+    ss << std::hex;
+    for (const char& c : str) {
+        ss << (int)c << " ";
+    }
+
+    return ss.str();
+}
+
+} // namespace
+
+MATCHER_P2(BytesEq, bytes, size, "") {
+    std::string expected;
+    expected.append((const char*)bytes, size);
+    std::string actual;
+    actual.append((const char*)arg, size);
+
+    *result_listener << "Bytes are not equal! \n";
+    *result_listener << "size: " << size << "\n";
+    *result_listener << "expected: " << dumpByteStringHex(expected).c_str() << "\n";
+    *result_listener << "actual: " << dumpByteStringHex(actual).c_str() << "\n";
+
+    return expected == actual;
+}
+
+TEST_F(TimeStatsTest, globalStatsCallback) {
+    constexpr size_t TOTAL_FRAMES = 5;
+    constexpr size_t MISSED_FRAMES = 4;
+    constexpr size_t CLIENT_COMPOSITION_FRAMES = 3;
+    constexpr size_t DISPLAY_EVENT_CONNECTIONS = 14;
+
+    mTimeStats->onBootFinished();
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    for (size_t i = 0; i < TOTAL_FRAMES; i++) {
+        mTimeStats->incrementTotalFrames();
+    }
+    for (size_t i = 0; i < MISSED_FRAMES; i++) {
+        mTimeStats->incrementMissedFrames();
+    }
+    for (size_t i = 0; i < CLIENT_COMPOSITION_FRAMES; i++) {
+        mTimeStats->incrementClientCompositionFrames();
+    }
+
+    mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS);
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats->recordFrameDuration(1000000, 3000000);
+    mTimeStats->recordRenderEngineDuration(2000000, 4000000);
+    mTimeStats->recordRenderEngineDuration(2000000, std::make_shared<FenceTime>(3000000));
+
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    std::string expectedFrameDuration = buildExpectedHistogramBytestring({2}, {1});
+    std::string expectedRenderEngineTiming = buildExpectedHistogramBytestring({1, 2}, {1, 1});
+
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventSetAtomId(mDelegate->mEvent,
+                                        android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, TOTAL_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, MISSED_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, CLIENT_COMPOSITION_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, _));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 2));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, DISPLAY_EVENT_CONNECTIONS));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedFrameDuration.c_str(),
+                                                     expectedFrameDuration.size()),
+                                             expectedFrameDuration.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedRenderEngineTiming.c_str(),
+                                                     expectedRenderEngineTiming.size()),
+                                             expectedRenderEngineTiming.size()));
+        EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                              mDelegate->mCookie));
+
+    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());
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) {
+    constexpr size_t LATE_ACQUIRE_FRAMES = 2;
+    constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
+        mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
+    }
+    for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
+        mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
+    }
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {1});
+    std::string expectedPostToPresent = buildExpectedHistogramBytestring({4}, {1});
+    std::string expectedAcquireToPresent = buildExpectedHistogramBytestring({3}, {1});
+    std::string expectedLatchToPresent = buildExpectedHistogramBytestring({2}, {1});
+    std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1});
+    std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1});
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventSetAtomId(mDelegate->mEvent,
+                                        android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteString8(mDelegate->mEvent,
+                                           StrEq(genLayerName(LAYER_ID_0).c_str())));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 1));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, 0));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedPresentToPresent.c_str(),
+                                                     expectedPresentToPresent.size()),
+                                             expectedPresentToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedPostToPresent.c_str(),
+                                                     expectedPostToPresent.size()),
+                                             expectedPostToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedAcquireToPresent.c_str(),
+                                                     expectedAcquireToPresent.size()),
+                                             expectedAcquireToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedLatchToPresent.c_str(),
+                                                     expectedLatchToPresent.size()),
+                                             expectedLatchToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedDesiredToPresent.c_str(),
+                                                     expectedDesiredToPresent.size()),
+                                             expectedDesiredToPresent.size()));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)expectedPostToAcquire.c_str(),
+                                                     expectedPostToAcquire.size()),
+                                             expectedPostToAcquire.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES));
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES));
+        EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent));
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+
+    SFTimeStatsGlobalProto globalProto;
+    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+    EXPECT_EQ(0, globalProto.stats_size());
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleLayers) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_CALL(*mDelegate,
+                statsEventSetAtomId(mDelegate->mEvent,
+                                    android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+            .Times(2);
+    EXPECT_CALL(*mDelegate,
+                statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_0).c_str())));
+    EXPECT_CALL(*mDelegate,
+                statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_pullsMultipleBuckets) {
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+    // Now make sure that TimeStats flushes global stats to register the
+    // callback.
+    mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
+    mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1, 2}, {2, 1});
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedPresentToPresent.c_str(),
+                                                     expectedPresentToPresent.size()),
+                                             expectedPresentToPresent.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+                .Times(AnyNumber());
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsHistogramBuckets) {
+    mDelegate = new FakeStatsEventDelegate;
+    mTimeStats =
+            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate),
+                                              std::nullopt, 1);
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    std::string expectedPresentToPresent = buildExpectedHistogramBytestring({1}, {2});
+    {
+        InSequence seq;
+        EXPECT_CALL(*mDelegate,
+                    statsEventWriteByteArray(mDelegate->mEvent,
+                                             BytesEq((const uint8_t*)
+                                                             expectedPresentToPresent.c_str(),
+                                                     expectedPresentToPresent.size()),
+                                             expectedPresentToPresent.size()));
+        EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, _, _))
+                .Times(AnyNumber());
+    }
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
+TEST_F(TimeStatsTest, layerStatsCallback_limitsLayers) {
+    mDelegate = new FakeStatsEventDelegate;
+    mTimeStats =
+            std::make_unique<impl::TimeStats>(std::unique_ptr<FakeStatsEventDelegate>(mDelegate), 1,
+                                              std::nullopt);
+    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+    mTimeStats->onBootFinished();
+
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 1, 2000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 2, 3000000);
+    insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_1, 4, 5000000);
+
+    EXPECT_THAT(mDelegate->mAtomTags,
+                UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+                                     android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+    EXPECT_NE(nullptr, mDelegate->mCallback);
+    EXPECT_EQ(mTimeStats.get(), mDelegate->mCookie);
+
+    EXPECT_CALL(*mDelegate,
+                statsEventSetAtomId(mDelegate->mEvent,
+                                    android::util::SURFACEFLINGER_STATS_LAYER_INFO))
+            .Times(1);
+    EXPECT_CALL(*mDelegate,
+                statsEventWriteString8(mDelegate->mEvent, StrEq(genLayerName(LAYER_ID_1).c_str())));
+    EXPECT_EQ(AStatsManager_PULL_SUCCESS,
+              mDelegate->makePullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+                                              mDelegate->mCookie));
+}
+
 TEST_F(TimeStatsTest, canSurviveMonkey) {
     if (g_noSlowTests) {
         GTEST_SKIP();
@@ -557,24 +1150,27 @@
     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 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));
+                ASSERT_NO_FATAL_FAILURE(mTimeStats->removeTimeRecord(layerId, frameNumber));
                 continue;
             case 1:
                 ALOGV("onDestroy");
-                ASSERT_NO_FATAL_FAILURE(mTimeStats->onDestroy(layerID));
+                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);
+        ALOGV("type[%d], layerId[%d], frameNumber[%d], ts[%d]", type, layerId, frameNumber, ts);
+        setTimeStamp(type, layerId, frameNumber, ts);
     }
 }
 
 } // namespace
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
new file mode 100644
index 0000000..f1739e5
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "CompositionTest"
+
+#include <compositionengine/Display.h>
+#include <compositionengine/mock/DisplaySurface.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <utils/String8.h>
+
+#include "TestableScheduler.h"
+#include "TestableSurfaceFlinger.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+
+namespace android {
+
+using testing::_;
+using testing::Return;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class TransactionApplicationTest : public testing::Test {
+public:
+    TransactionApplicationTest() {
+        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.mutableEventQueue().reset(mMessageQueue);
+        setupScheduler();
+    }
+
+    ~TransactionApplicationTest() {
+        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 setupScheduler() {
+        auto eventThread = std::make_unique<mock::EventThread>();
+        auto sfEventThread = std::make_unique<mock::EventThread>();
+
+        EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*eventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(eventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+        EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+                .WillOnce(Return(
+                        new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+                                                  ISurfaceComposer::eConfigChangedSuppress)));
+
+        EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+        EXPECT_CALL(*mPrimaryDispSync, getPeriod())
+                .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+
+        mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync),
+                                std::make_unique<mock::EventControlThread>(),
+                                std::move(eventThread), std::move(sfEventThread));
+    }
+
+    TestableScheduler* mScheduler;
+    TestableSurfaceFlinger mFlinger;
+
+    std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
+    mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
+
+    mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+    mock::DispSync* mPrimaryDispSync = new mock::DispSync();
+
+    struct TransactionInfo {
+        Vector<ComposerState> states;
+        Vector<DisplayState> displays;
+        uint32_t flags = 0;
+        sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+        InputWindowCommands inputWindowCommands;
+        int64_t desiredPresentTime = -1;
+        client_cache_t uncacheBuffer;
+    };
+
+    void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) {
+        EXPECT_EQ(0, info.states.size());
+        EXPECT_EQ(0, state.states.size());
+
+        EXPECT_EQ(0, info.displays.size());
+        EXPECT_EQ(0, state.displays.size());
+        EXPECT_EQ(info.flags, state.flags);
+        EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
+    }
+
+    void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows,
+                     int64_t desiredPresentTime) {
+        mTransactionNumber++;
+        transaction.flags |= flags; // ISurfaceComposer::eSynchronous;
+        transaction.inputWindowCommands.syncInputWindows = syncInputWindows;
+        transaction.desiredPresentTime = desiredPresentTime;
+    }
+
+    void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
+        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        // called in SurfaceFlinger::signalTransaction
+        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillOnce(Return(systemTime()));
+        TransactionInfo transaction;
+        setupSingle(transaction, flags, syncInputWindows,
+                    /*desiredPresentTime*/ -1);
+        nsecs_t applicationTime = systemTime();
+        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        // This transaction should not have been placed on the transaction queue.
+        // If transaction is synchronous or syncs input windows, SF
+        // applyTransactionState should time out (5s) wating for SF to commit
+        // the transaction or to receive a signal that syncInputWindows has
+        // completed.  If this is animation, it should not time out waiting.
+        nsecs_t returnedTime = systemTime();
+        if (flags & ISurfaceComposer::eSynchronous || syncInputWindows) {
+            EXPECT_GE(returnedTime, applicationTime + s2ns(5));
+        } else {
+            EXPECT_LE(returnedTime, applicationTime + s2ns(5));
+        }
+        auto transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_EQ(0, transactionQueue.size());
+    }
+
+    void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
+        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        // called in SurfaceFlinger::signalTransaction
+        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+        // first check will see desired present time has not passed,
+        // but afterwards it will look like the desired present time has passed
+        nsecs_t time = systemTime();
+        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime())
+                .WillOnce(Return(time + nsecs_t(5 * 1e8)));
+        TransactionInfo transaction;
+        setupSingle(transaction, flags, syncInputWindows,
+                    /*desiredPresentTime*/ time + s2ns(1));
+        nsecs_t applicationSentTime = systemTime();
+        mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags,
+                                     transaction.applyToken, transaction.inputWindowCommands,
+                                     transaction.desiredPresentTime, transaction.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        nsecs_t returnedTime = systemTime();
+        EXPECT_LE(returnedTime, applicationSentTime + s2ns(5));
+        // This transaction should have been placed on the transaction queue
+        auto transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_EQ(1, transactionQueue.size());
+    }
+
+    void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) {
+        ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+        // called in SurfaceFlinger::signalTransaction
+        nsecs_t time = systemTime();
+        EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+        EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime())
+                .WillOnce(Return(time + nsecs_t(5 * 1e8)));
+        // transaction that should go on the pending thread
+        TransactionInfo transactionA;
+        setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
+                    /*desiredPresentTime*/ time + s2ns(1));
+
+        // transaction that would not have gone on the pending thread if not
+        // blocked
+        TransactionInfo transactionB;
+        setupSingle(transactionB, flags, syncInputWindows,
+                    /*desiredPresentTime*/ -1);
+
+        nsecs_t applicationSentTime = systemTime();
+        mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+                                     transactionA.applyToken, transactionA.inputWindowCommands,
+                                     transactionA.desiredPresentTime, transactionA.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        // This thread should not have been blocked by the above transaction
+        // (5s is the timeout period that applyTransactionState waits for SF to
+        // commit the transaction)
+        EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
+
+        applicationSentTime = systemTime();
+        mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags,
+                                     transactionB.applyToken, transactionB.inputWindowCommands,
+                                     transactionB.desiredPresentTime, transactionB.uncacheBuffer,
+                                     mHasListenerCallbacks, mCallbacks);
+
+        // this thread should have been blocked by the above transaction
+        // if this is an animation, this thread should be blocked for 5s
+        // in setTransactionState waiting for transactionA to flush.  Otherwise,
+        // the transaction should be placed on the pending queue
+        if (flags & ISurfaceComposer::eAnimation) {
+            EXPECT_GE(systemTime(), applicationSentTime + s2ns(5));
+        } else {
+            EXPECT_LE(systemTime(), applicationSentTime + s2ns(5));
+        }
+
+        // check that there is one binder on the pending queue.
+        auto transactionQueue = mFlinger.getTransactionQueue();
+        EXPECT_EQ(1, transactionQueue.size());
+
+        auto& [applyToken, transactionStates] = *(transactionQueue.begin());
+        EXPECT_EQ(2, transactionStates.size());
+
+        auto& transactionStateA = transactionStates.front();
+        transactionStates.pop();
+        checkEqual(transactionA, transactionStateA);
+        auto& transactionStateB = transactionStates.front();
+        checkEqual(transactionB, transactionStateB);
+    }
+
+    bool mHasListenerCallbacks = false;
+    std::vector<ListenerCallbacks> mCallbacks;
+    int mTransactionNumber = 0;
+};
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
+    ASSERT_EQ(0, mFlinger.getTransactionQueue().size());
+    // called in SurfaceFlinger::signalTransaction
+    EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+    // nsecs_t time = systemTime();
+    EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime())
+            .WillOnce(Return(nsecs_t(5 * 1e8)))
+            .WillOnce(Return(s2ns(2)));
+    TransactionInfo transactionA; // transaction to go on pending queue
+    setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
+                /*desiredPresentTime*/ s2ns(1));
+    mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags,
+                                 transactionA.applyToken, transactionA.inputWindowCommands,
+                                 transactionA.desiredPresentTime, transactionA.uncacheBuffer,
+                                 mHasListenerCallbacks, mCallbacks);
+
+    auto& transactionQueue = mFlinger.getTransactionQueue();
+    ASSERT_EQ(1, transactionQueue.size());
+
+    auto& [applyToken, transactionStates] = *(transactionQueue.begin());
+    ASSERT_EQ(1, transactionStates.size());
+
+    auto& transactionState = transactionStates.front();
+    checkEqual(transactionA, transactionState);
+
+    // because flushing uses the cached expected present time, we send an empty
+    // transaction here (sending a null applyToken to fake it as from a
+    // different process) to re-query and reset the cached expected present time
+    TransactionInfo empty;
+    empty.applyToken = sp<IBinder>();
+    mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken,
+                                 empty.inputWindowCommands, empty.desiredPresentTime,
+                                 empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks);
+
+    // flush transaction queue should flush as desiredPresentTime has
+    // passed
+    mFlinger.flushTransactionQueues();
+
+    EXPECT_EQ(0, transactionQueue.size());
+}
+
+TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) {
+    NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Animation) {
+    NotPlacedOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) {
+    NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
+}
+
+TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) {
+    PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Animation) {
+    PlaceOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) {
+    PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true);
+}
+
+TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) {
+    BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Animation) {
+    BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false);
+}
+
+TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_SyncInputWindows) {
+    BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true);
+}
+
+TEST_F(TransactionApplicationTest, FromHandle) {
+    sp<IBinder> badHandle;
+    auto ret = mFlinger.fromHandle(badHandle);
+    EXPECT_EQ(nullptr, ret.get());
+}
+} // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
new file mode 100644
index 0000000..caac61d
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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 "Scheduler/TimeKeeper.h"
+#include "Scheduler/Timer.h"
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+template <typename Rep, typename Per>
+constexpr nsecs_t toNs(std::chrono::duration<Rep, Per> const& tp) {
+    return std::chrono::duration_cast<std::chrono::nanoseconds>(tp).count();
+}
+
+class FixedRateIdealStubTracker : public VSyncTracker {
+public:
+    FixedRateIdealStubTracker() : mPeriod{toNs(3ms)} {}
+
+    bool addVsyncTimestamp(nsecs_t) final { return true; }
+
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+        auto const floor = timePoint % mPeriod;
+        if (floor == 0) {
+            return timePoint;
+        }
+        return timePoint - floor + mPeriod;
+    }
+
+    nsecs_t currentPeriod() const final { return mPeriod; }
+
+    void setPeriod(nsecs_t) final {}
+    void resetModel() final {}
+
+private:
+    nsecs_t const mPeriod;
+};
+
+class VRRStubTracker : public VSyncTracker {
+public:
+    VRRStubTracker(nsecs_t period) : mPeriod{period} {}
+
+    bool addVsyncTimestamp(nsecs_t) final { return true; }
+
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        auto const normalized_to_base = time_point - mBase;
+        auto const floor = (normalized_to_base) % mPeriod;
+        if (floor == 0) {
+            return time_point;
+        }
+        return normalized_to_base - floor + mPeriod + mBase;
+    }
+
+    void set_interval(nsecs_t interval, nsecs_t last_known) {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        mPeriod = interval;
+        mBase = last_known;
+    }
+
+    nsecs_t currentPeriod() const final {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        return mPeriod;
+    }
+
+    void setPeriod(nsecs_t) final {}
+    void resetModel() final {}
+
+private:
+    std::mutex mutable mMutex;
+    nsecs_t mPeriod;
+    nsecs_t mBase = 0;
+};
+
+struct VSyncDispatchRealtimeTest : testing::Test {
+    static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
+    static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
+    static size_t constexpr mIterations = 20;
+};
+
+class RepeatingCallbackReceiver {
+public:
+    RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl)
+          : mWorkload(wl),
+            mCallback(
+                    dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {}
+
+    void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) {
+        mCallbackTimes.reserve(iterations);
+        mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload);
+
+        for (auto i = 0u; i < iterations - 1; i++) {
+            std::unique_lock<decltype(mMutex)> lk(mMutex);
+            mCv.wait(lk, [&] { return mCalled; });
+            mCalled = false;
+            auto last = mLastTarget;
+            lk.unlock();
+
+            onEachFrame(last);
+
+            mCallback.schedule(mWorkload, last + mWorkload);
+        }
+
+        // wait for the last callback.
+        std::unique_lock<decltype(mMutex)> lk(mMutex);
+        mCv.wait(lk, [&] { return mCalled; });
+    }
+
+    void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const {
+        fn(mCallbackTimes);
+    }
+
+private:
+    void callback_called(nsecs_t time) {
+        std::lock_guard<decltype(mMutex)> lk(mMutex);
+        mCallbackTimes.push_back(time);
+        mCalled = true;
+        mLastTarget = time;
+        mCv.notify_all();
+    }
+
+    nsecs_t const mWorkload;
+    VSyncCallbackRegistration mCallback;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    bool mCalled = false;
+    nsecs_t mLastTarget = 0;
+    std::vector<nsecs_t> mCallbackTimes;
+};
+
+TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
+    FixedRateIdealStubTracker tracker;
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
+
+    static size_t constexpr num_clients = 3;
+    std::array<RepeatingCallbackReceiver, num_clients>
+            cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)),
+                        RepeatingCallbackReceiver(dispatch, toNs(0h)),
+                        RepeatingCallbackReceiver(dispatch, toNs(1ms))};
+
+    auto const on_each_frame = [](nsecs_t) {};
+    std::array<std::thread, num_clients> threads{
+            std::thread([&] { cb_receiver[0].repeatedly_schedule(mIterations, on_each_frame); }),
+            std::thread([&] { cb_receiver[1].repeatedly_schedule(mIterations, on_each_frame); }),
+            std::thread([&] { cb_receiver[2].repeatedly_schedule(mIterations, on_each_frame); }),
+    };
+
+    for (auto it = threads.rbegin(); it != threads.rend(); it++) {
+        it->join();
+    }
+
+    for (auto const& cbs : cb_receiver) {
+        cbs.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+    }
+}
+
+// starts at 333hz, slides down to 43hz
+TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
+    auto next_vsync_interval = toNs(3ms);
+    VRRStubTracker tracker(next_vsync_interval);
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
+
+    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+
+    auto const on_each_frame = [&](nsecs_t last_known) {
+        tracker.set_interval(next_vsync_interval += toNs(1ms), last_known);
+    };
+
+    std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
+    eventThread.join();
+
+    cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+}
+
+// starts at 333hz, jumps to 200hz at frame 10
+TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
+    VRRStubTracker tracker(toNs(3ms));
+    VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+                                     mVsyncMoveThreshold);
+
+    RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
+
+    auto jump_frame_counter = 0u;
+    auto constexpr jump_frame_at = 10u;
+    auto const on_each_frame = [&](nsecs_t last_known) {
+        if (jump_frame_counter++ == jump_frame_at) {
+            tracker.set_interval(toNs(5ms), last_known);
+        }
+    };
+    std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); });
+    eventThread.join();
+
+    cb_receiver.with_callback_times([](auto times) { EXPECT_THAT(times.size(), Eq(mIterations)); });
+}
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
new file mode 100644
index 0000000..3ab38e4
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -0,0 +1,820 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatchTimerQueue.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+    MockVSyncTracker(nsecs_t period) : mPeriod{period} {
+        ON_CALL(*this, nextAnticipatedVSyncTimeFrom(_))
+                .WillByDefault(Invoke(this, &MockVSyncTracker::nextVSyncTime));
+        ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true));
+    }
+
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+    MOCK_METHOD0(resetModel, void());
+
+    nsecs_t nextVSyncTime(nsecs_t timePoint) const {
+        if (timePoint % mPeriod == 0) {
+            return timePoint;
+        }
+        return (timePoint - (timePoint % mPeriod) + mPeriod);
+    }
+
+protected:
+    nsecs_t const mPeriod;
+};
+
+class ControllableClock : public TimeKeeper {
+public:
+    ControllableClock() {
+        ON_CALL(*this, alarmIn(_, _))
+                .WillByDefault(Invoke(this, &ControllableClock::alarmInDefaultBehavior));
+        ON_CALL(*this, now()).WillByDefault(Invoke(this, &ControllableClock::fakeTime));
+    }
+
+    MOCK_CONST_METHOD0(now, nsecs_t());
+    MOCK_METHOD2(alarmIn, void(std::function<void()> const&, nsecs_t time));
+    MOCK_METHOD0(alarmCancel, void());
+
+    void alarmInDefaultBehavior(std::function<void()> const& callback, nsecs_t time) {
+        mCallback = callback;
+        mNextCallbackTime = time + mCurrentTime;
+    }
+
+    nsecs_t fakeTime() const { return mCurrentTime; }
+
+    void advanceToNextCallback() {
+        mCurrentTime = mNextCallbackTime;
+        if (mCallback) {
+            mCallback();
+        }
+    }
+
+    void advanceBy(nsecs_t advancement) {
+        mCurrentTime += advancement;
+        if (mCurrentTime >= mNextCallbackTime && mCallback) {
+            mCallback();
+        }
+    };
+
+private:
+    std::function<void()> mCallback;
+    nsecs_t mNextCallbackTime = 0;
+    nsecs_t mCurrentTime = 0;
+};
+
+class CountingCallback {
+public:
+    CountingCallback(VSyncDispatch& dispatch)
+          : mDispatch(dispatch),
+            mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this,
+                                                       std::placeholders::_1,
+                                                       std::placeholders::_2),
+                                             "test")) {}
+    ~CountingCallback() { mDispatch.unregisterCallback(mToken); }
+
+    operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+    void counter(nsecs_t time, nsecs_t) { mCalls.push_back(time); }
+
+    VSyncDispatch& mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    std::vector<nsecs_t> mCalls;
+};
+
+class PausingCallback {
+public:
+    PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount)
+          : mDispatch(dispatch),
+            mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this,
+                                                       std::placeholders::_1,
+                                                       std::placeholders::_2),
+                                             "test")),
+            mRegistered(true),
+            mPauseAmount(pauseAmount) {}
+    ~PausingCallback() { unregister(); }
+
+    operator VSyncDispatch::CallbackToken() const { return mToken; }
+
+    void pause(nsecs_t, nsecs_t) {
+        std::unique_lock<std::mutex> lk(mMutex);
+        mPause = true;
+        mCv.notify_all();
+
+        mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; });
+
+        mResourcePresent = (mResource.lock() != nullptr);
+    }
+
+    bool waitForPause() {
+        std::unique_lock<std::mutex> lk(mMutex);
+        auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; });
+        return waiting;
+    }
+
+    void stashResource(std::weak_ptr<void> const& resource) { mResource = resource; }
+
+    bool resourcePresent() { return mResourcePresent; }
+
+    void unpause() {
+        std::unique_lock<std::mutex> lk(mMutex);
+        mPause = false;
+        mCv.notify_all();
+    }
+
+    void unregister() {
+        if (mRegistered) {
+            mDispatch.unregisterCallback(mToken);
+            mRegistered = false;
+        }
+    }
+
+    VSyncDispatch& mDispatch;
+    VSyncDispatch::CallbackToken mToken;
+    bool mRegistered = true;
+
+    std::mutex mMutex;
+    std::condition_variable mCv;
+    bool mPause = false;
+    std::weak_ptr<void> mResource;
+    bool mResourcePresent = false;
+    std::chrono::milliseconds const mPauseAmount;
+};
+
+class VSyncDispatchTimerQueueTest : public testing::Test {
+protected:
+    std::unique_ptr<TimeKeeper> createTimeKeeper() {
+        class TimeKeeperWrapper : public TimeKeeper {
+        public:
+            TimeKeeperWrapper(TimeKeeper& control) : mControllableClock(control) {}
+            void alarmIn(std::function<void()> const& callback, nsecs_t time) final {
+                mControllableClock.alarmIn(callback, time);
+            }
+            void alarmCancel() final { mControllableClock.alarmCancel(); }
+            nsecs_t now() const final { return mControllableClock.now(); }
+
+        private:
+            TimeKeeper& mControllableClock;
+        };
+        return std::make_unique<TimeKeeperWrapper>(mMockClock);
+    }
+
+    ~VSyncDispatchTimerQueueTest() {
+        // destructor of  dispatch will cancelAlarm(). Ignore final cancel in common test.
+        Mock::VerifyAndClearExpectations(&mMockClock);
+    }
+
+    void advanceToNextCallback() { mMockClock.advanceToNextCallback(); }
+
+    NiceMock<ControllableClock> mMockClock;
+    static nsecs_t constexpr mDispatchGroupThreshold = 5;
+    nsecs_t const mPeriod = 1000;
+    nsecs_t const mVsyncMoveThreshold = 300;
+    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+    VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                      mVsyncMoveThreshold};
+};
+
+TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+    {
+        VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+                                          mVsyncMoveThreshold};
+        CountingCallback cb(mDispatch);
+        EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
+    }
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) {
+    auto intended = mPeriod - 230;
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150));
+    EXPECT_CALL(mMockClock, alarmIn(_, 1050));
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 100, mPeriod);
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb.mCalls[0], Eq(1150));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) {
+    auto const now = 234;
+    mMockClock.advanceBy(234);
+    auto const workDuration = 10 * mPeriod;
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration))
+            .WillOnce(Return(mPeriod * 11));
+    EXPECT_CALL(mMockClock, alarmIn(_, mPeriod - now));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(950);
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s));
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+    std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+    EXPECT_TRUE(cb.waitForPause());
+    EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate);
+    cb.unpause();
+    pausingThread.join();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    auto resource = std::make_shared<int>(110);
+
+    PausingCallback cb(mDispatch, 50ms);
+    cb.stashResource(resource);
+    EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled);
+
+    std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); });
+    EXPECT_TRUE(cb.waitForPause());
+
+    cb.unregister();
+    resource.reset();
+
+    cb.unpause();
+    pausingThread.join();
+
+    EXPECT_TRUE(cb.resourcePresent());
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .Times(4)
+            .WillOnce(Return(1055))
+            .WillOnce(Return(1063))
+            .WillOnce(Return(1063))
+            .WillOnce(Return(1075));
+
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 955)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 813)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 162)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, mPeriod);
+    mDispatch.schedule(cb1, 250, mPeriod);
+
+    advanceToNextCallback();
+    advanceToNextCallback();
+
+    ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb0.mCalls[0], Eq(1075));
+    ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb1.mCalls[0], Eq(1063));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(4)
+            .WillOnce(Return(10000))
+            .WillOnce(Return(1000))
+            .WillOnce(Return(10000))
+            .WillOnce(Return(10000));
+
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 750)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 9900)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, mPeriod * 10);
+    mDispatch.schedule(cb1, 250, mPeriod);
+    mDispatch.cancel(cb1);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, 300, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 100)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, 500, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 990)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 10)).InSequence(seq);
+
+    auto offset = 400;
+    auto closeOffset = offset + mDispatchGroupThreshold - 1;
+    auto notCloseOffset = offset + 2 * mDispatchGroupThreshold;
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 400, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    mDispatch.schedule(cb1, closeOffset, 1000);
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb0.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb0.mCalls[0], Eq(mPeriod));
+    ASSERT_THAT(cb1.mCalls.size(), Eq(1));
+    EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod));
+
+    mDispatch.schedule(cb0, 400, 2000);
+    mDispatch.schedule(cb1, notCloseOffset, 2000);
+    advanceToNextCallback();
+    ASSERT_THAT(cb1.mCalls.size(), Eq(2));
+    EXPECT_THAT(cb1.mCalls[1], Eq(2000));
+
+    advanceToNextCallback();
+    ASSERT_THAT(cb0.mCalls.size(), Eq(2));
+    EXPECT_THAT(cb0.mCalls[1], Eq(2000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 900));
+    EXPECT_CALL(mMockClock, alarmIn(_, 800));
+    EXPECT_CALL(mMockClock, alarmIn(_, 100));
+    EXPECT_CALL(mMockClock, alarmCancel());
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 100, 1000);
+    mDispatch.schedule(cb1, 200, 1000);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(3)
+            .WillOnce(Return(950))
+            .WillOnce(Return(1975))
+            .WillOnce(Return(2950));
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 100, 920);
+
+    mMockClock.advanceBy(850);
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+
+    mDispatch.schedule(cb, 100, 1900);
+    mMockClock.advanceBy(900);
+    EXPECT_THAT(cb.mCalls.size(), Eq(1));
+    mMockClock.advanceBy(125);
+    EXPECT_THAT(cb.mCalls.size(), Eq(2));
+
+    mDispatch.schedule(cb, 100, 2900);
+    mMockClock.advanceBy(975);
+    EXPECT_THAT(cb.mCalls.size(), Eq(3));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+
+    VSyncDispatch::CallbackToken tmp;
+    tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); },
+                                     "o.o");
+
+    mDispatch.schedule(tmp, 100, 1000);
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
+    VSyncDispatch::CallbackToken tmp;
+    std::optional<nsecs_t> lastTarget;
+    tmp = mDispatch.registerCallback(
+            [&](auto timestamp, auto) {
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+                          ScheduleResult::Scheduled);
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
+                EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+                          ScheduleResult::Scheduled);
+                lastTarget = timestamp;
+            },
+            "oo");
+
+    mDispatch.schedule(tmp, 999, 1000);
+    advanceToNextCallback();
+    EXPECT_THAT(lastTarget, Eq(1000));
+
+    advanceToNextCallback();
+    EXPECT_THAT(lastTarget, Eq(2000));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 200)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 1000)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 150)).InSequence(seq);
+
+    CountingCallback cb(mDispatch);
+    mDispatch.schedule(cb, 0, 1000);
+
+    mMockClock.advanceBy(750);
+    mDispatch.schedule(cb, 50, 1000);
+
+    advanceToNextCallback();
+    mDispatch.schedule(cb, 50, 2000);
+
+    mMockClock.advanceBy(800);
+    mDispatch.schedule(cb, 100, 2000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, lateModifications) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 400)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 350)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 950)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.schedule(cb1, 100, 1000);
+
+    advanceToNextCallback();
+    mDispatch.schedule(cb0, 200, 2000);
+    mDispatch.schedule(cb1, 150, 1000);
+
+    advanceToNextCallback();
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    CountingCallback cb1(mDispatch);
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.schedule(cb1, 500, 20000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmCancel()).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 900)).InSequence(seq);
+
+    CountingCallback cb0(mDispatch);
+    mDispatch.schedule(cb0, 500, 1000);
+    mDispatch.cancel(cb0);
+    mDispatch.schedule(cb0, 100, 1000);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) {
+    VSyncDispatch::CallbackToken token(100);
+    EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error));
+    EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+}
+
+// b/1450138150
+TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500));
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(400);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+            .Times(2)
+            .WillOnce(Return(1000))
+            .WillOnce(Return(1002));
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+    mMockClock.advanceBy(400);
+    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
+    Sequence seq;
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+    EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+    CountingCallback cb0(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+    advanceToNextCallback();
+    EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 600));
+
+    CountingCallback cb(mDispatch);
+    EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+
+    EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+
+    advanceToNextCallback();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+    VSyncCallbackRegistration cb(
+            mDispatch, [](auto, auto) {}, "");
+    VSyncCallbackRegistration cb1(std::move(cb));
+    cb.schedule(100, 1000);
+    cb.cancel();
+
+    cb1.schedule(500, 1000);
+    cb1.cancel();
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) {
+    EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
+    EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
+
+    VSyncCallbackRegistration cb(
+            mDispatch, [](auto, auto) {}, "");
+    VSyncCallbackRegistration cb1(
+            mDispatch, [](auto, auto) {}, "");
+    cb1 = std::move(cb);
+    cb.schedule(100, 1000);
+    cb.cancel();
+
+    cb1.schedule(500, 1000);
+    cb1.cancel();
+}
+
+class VSyncDispatchTimerQueueEntryTest : public testing::Test {
+protected:
+    nsecs_t const mPeriod = 1000;
+    nsecs_t const mVsyncMoveThreshold = 200;
+    NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
+};
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
+    std::string name("basicname");
+    VSyncDispatchTimerQueueEntry entry(
+            name, [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.name(), Eq(name));
+    EXPECT_FALSE(entry.lastExecutedVsyncTarget());
+    EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    EXPECT_FALSE(entry.wakeupTime());
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(900));
+
+    entry.disarm();
+    EXPECT_FALSE(entry.wakeupTime());
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) {
+    auto const duration = 500;
+    auto const now = 8750;
+
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
+            .Times(1)
+            .WillOnce(Return(10000));
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    EXPECT_FALSE(entry.wakeupTime());
+    EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(9500));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
+    auto callCount = 0;
+    auto vsyncCalledTime = 0;
+    auto wakeupCalledTime = 0;
+    VSyncDispatchTimerQueueEntry entry(
+            "test",
+            [&](auto vsyncTime, auto wakeupTime) {
+                callCount++;
+                vsyncCalledTime = vsyncTime;
+                wakeupCalledTime = wakeupTime;
+            },
+            mVsyncMoveThreshold);
+
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(900));
+
+    entry.callback(entry.executing(), *wakeup);
+
+    EXPECT_THAT(callCount, Eq(1));
+    EXPECT_THAT(vsyncCalledTime, Eq(mPeriod));
+    EXPECT_THAT(wakeupCalledTime, Eq(*wakeup));
+    EXPECT_FALSE(entry.wakeupTime());
+    auto lastCalledTarget = entry.lastExecutedVsyncTarget();
+    ASSERT_TRUE(lastCalledTarget);
+    EXPECT_THAT(*lastCalledTarget, Eq(mPeriod));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) {
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(2)
+            .WillOnce(Return(1000))
+            .WillOnce(Return(1020));
+
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    EXPECT_FALSE(entry.wakeupTime());
+    entry.update(mStubTracker, 0);
+    EXPECT_FALSE(entry.wakeupTime());
+
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    auto wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(wakeup, Eq(900));
+
+    entry.update(mStubTracker, 0);
+    wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(920));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    entry.update(mStubTracker, 0);
+
+    auto const wakeup = entry.wakeupTime();
+    ASSERT_TRUE(wakeup);
+    EXPECT_THAT(*wakeup, Eq(wakeup));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    entry.executing(); // 1000 is executing
+    // had 1000 not been executing, this could have been scheduled for time 800.
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+
+    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+
+    EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest,
+       willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+
+    Sequence seq;
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+            .InSequence(seq)
+            .WillOnce(Return(1000));
+    EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+            .InSequence(seq)
+            .WillOnce(Return(2000));
+
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+
+    entry.executing(); // 1000 is executing
+
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
+    VSyncDispatchTimerQueueEntry entry(
+            "test", [](auto, auto) {}, mVsyncMoveThreshold);
+    EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+    EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
new file mode 100644
index 0000000..f834af8
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -0,0 +1,396 @@
+/*
+ * 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.
+ */
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/VSyncPredictor.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <algorithm>
+#include <chrono>
+#include <utility>
+
+using namespace testing;
+using namespace std::literals;
+
+namespace android::scheduler {
+
+MATCHER_P2(IsCloseTo, value, tolerance, "is within tolerance") {
+    return arg <= value + tolerance && arg >= value - tolerance;
+}
+
+std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs_t bias) {
+    std::vector<nsecs_t> vsyncs(count);
+    std::generate(vsyncs.begin(), vsyncs.end(),
+                  [&, n = 0]() mutable { return n++ * period + bias; });
+    return vsyncs;
+}
+
+struct VSyncPredictorTest : testing::Test {
+    nsecs_t mNow = 0;
+    nsecs_t mPeriod = 1000;
+    static constexpr size_t kHistorySize = 10;
+    static constexpr size_t kMinimumSamplesForPrediction = 6;
+    static constexpr size_t kOutlierTolerancePercent = 25;
+    static constexpr nsecs_t mMaxRoundingError = 100;
+
+    VSyncPredictor tracker{mPeriod, kHistorySize, kMinimumSamplesForPrediction,
+                           kOutlierTolerancePercent};
+};
+
+TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) {
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+
+    EXPECT_THAT(slope, Eq(mPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+
+    auto const changedPeriod = 2000;
+    tracker.setPeriod(changedPeriod);
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(changedPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+}
+
+TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) {
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.needsMoreSamples(mNow += mPeriod));
+        tracker.addVsyncTimestamp(mNow);
+    }
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+}
+
+TEST_F(VSyncPredictorTest, reportsSamplesNeededAfterExplicitRateChange) {
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(mNow += mPeriod);
+    }
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+
+    auto const changedPeriod = mPeriod * 2;
+    tracker.setPeriod(changedPeriod);
+    EXPECT_TRUE(tracker.needsMoreSamples(mNow));
+
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_TRUE(tracker.needsMoreSamples(mNow += changedPeriod));
+        tracker.addVsyncTimestamp(mNow);
+    }
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+}
+
+TEST_F(VSyncPredictorTest, transitionsToModelledPointsAfterSynthetic) {
+    auto last = mNow;
+    auto const bias = 10;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+        mNow += mPeriod - bias;
+        last = mNow;
+        tracker.addVsyncTimestamp(mNow);
+        mNow += bias;
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias));
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias));
+}
+
+TEST_F(VSyncPredictorTest, uponNotifiedOfInaccuracyUsesSynthetic) {
+    auto const slightlyLessPeriod = mPeriod - 10;
+    auto const changedPeriod = mPeriod - 1;
+    for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(mNow += slightlyLessPeriod);
+    }
+
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + slightlyLessPeriod));
+    tracker.setPeriod(changedPeriod);
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + changedPeriod));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_60hzHighVariance) {
+    // these are precomputed simulated 16.6s vsyncs with uniform distribution +/- 1.6ms error
+    std::vector<nsecs_t> const simulatedVsyncs{
+            15492949,  32325658,  49534984,  67496129,  84652891,
+            100332564, 117737004, 132125931, 149291099, 165199602,
+    };
+    auto constexpr idealPeriod = 16600000;
+    auto constexpr expectedPeriod = 16639242;
+    auto constexpr expectedIntercept = 1049341;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelines_90hzLowVariance) {
+    // these are precomputed simulated 11.1 vsyncs with uniform distribution +/- 1ms error
+    std::vector<nsecs_t> const simulatedVsyncs{
+            11167047, 22603464, 32538479, 44938134, 56321268,
+            66730346, 78062637, 88171429, 99707843, 111397621,
+    };
+    auto idealPeriod = 11110000;
+    auto expectedPeriod = 11089413;
+    auto expectedIntercept = 94421;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, adaptsToFenceTimelinesDiscontinuous_22hzLowVariance) {
+    // these are 11.1s vsyncs with low variance, randomly computed, between -1 and 1ms
+    std::vector<nsecs_t> const simulatedVsyncs{
+            45259463,   // 0
+            91511026,   // 1
+            136307650,  // 2
+            1864501714, // 40
+            1908641034, // 41
+            1955278544, // 42
+            4590180096, // 100
+            4681594994, // 102
+            5499224734, // 120
+            5591378272, // 122
+    };
+    auto idealPeriod = 45454545;
+    auto expectedPeriod = 45450152;
+    auto expectedIntercept = 469647;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, againstOutliersDiscontinuous_500hzLowVariance) {
+    std::vector<nsecs_t> const simulatedVsyncs{
+            1992548,    // 0
+            4078038,    // 1
+            6165794,    // 2
+            7958171,    // 3
+            10193537,   // 4
+            2401840200, // 1200
+            2403000000, // an outlier that should be excluded (1201 and a half)
+            2405803629, // 1202
+            2408028599, // 1203
+            2410121051, // 1204
+    };
+    auto idealPeriod = 2000000;
+    auto expectedPeriod = 1999892;
+    auto expectedIntercept = 175409;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, handlesVsyncChange) {
+    auto const fastPeriod = 100;
+    auto const fastTimeBase = 100;
+    auto const slowPeriod = 400;
+    auto const slowTimeBase = 800;
+    auto const simulatedVsyncsFast =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod, fastTimeBase);
+    auto const simulatedVsyncsSlow =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase);
+
+    tracker.setPeriod(fastPeriod);
+    for (auto const& timestamp : simulatedVsyncsFast) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto const mMaxRoundingError = 100;
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+
+    tracker.setPeriod(slowPeriod);
+    for (auto const& timestamp : simulatedVsyncsSlow) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) {
+    auto const fastPeriod = 101000;
+    auto const fastTimeBase = fastPeriod - 500;
+    auto const fastPeriod2 = 99000;
+
+    auto const slowPeriod = 400000;
+    auto const slowTimeBase = 800000 - 201;
+    auto const simulatedVsyncsFast =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod, fastTimeBase);
+    auto const simulatedVsyncsSlow =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, slowPeriod, slowTimeBase);
+    auto const simulatedVsyncsFast2 =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction, fastPeriod2, fastTimeBase);
+
+    auto idealPeriod = 100000;
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncsFast) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(fastPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+
+    tracker.setPeriod(slowPeriod);
+    for (auto const& timestamp : simulatedVsyncsSlow) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    // we had a model for 100ns mPeriod before, use that until the new samples are
+    // sufficiently built up
+    tracker.setPeriod(idealPeriod);
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(fastPeriod));
+    EXPECT_THAT(intercept, Eq(0));
+
+    for (auto const& timestamp : simulatedVsyncsFast2) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    std::tie(slope, intercept) = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, Eq(fastPeriod2));
+    EXPECT_THAT(intercept, Eq(0));
+}
+
+TEST_F(VSyncPredictorTest, willBecomeInaccurateAfterA_longTimeWithNoSamples) {
+    auto const simulatedVsyncs = generateVsyncTimestamps(kMinimumSamplesForPrediction, mPeriod, 0);
+
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+    auto const mNow = *simulatedVsyncs.rbegin();
+    EXPECT_FALSE(tracker.needsMoreSamples(mNow));
+
+    // TODO: would be better to decay this as a result of the variance of the samples
+    static auto constexpr aLongTimeOut = 1000000000;
+    EXPECT_TRUE(tracker.needsMoreSamples(mNow + aLongTimeOut));
+}
+
+TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) {
+    auto const simulatedVsyncs =
+            generateVsyncTimestamps(kMinimumSamplesForPrediction + 1, mPeriod, 0);
+    nsecs_t const mNow = 0;
+    EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mPeriod));
+
+    nsecs_t const aBitOfTime = 422;
+
+    for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(simulatedVsyncs[i]);
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(simulatedVsyncs[i] + aBitOfTime),
+                    Eq(mPeriod + simulatedVsyncs[i]));
+    }
+
+    for (auto i = kMinimumSamplesForPrediction; i < simulatedVsyncs.size(); i++) {
+        tracker.addVsyncTimestamp(simulatedVsyncs[i]);
+        EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(simulatedVsyncs[i] + aBitOfTime),
+                    Eq(mPeriod + simulatedVsyncs[i]));
+    }
+}
+
+// See b/145667109, and comment in prod code under test.
+TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
+    std::vector<nsecs_t> const simulatedVsyncs{
+            158929578733000,
+            158929306806205, // oldest TS in ringbuffer
+            158929650879052,
+            158929661969209,
+            158929684198847,
+            158929695268171,
+            158929706370359,
+    };
+    auto const idealPeriod = 11111111;
+    auto const expectedPeriod = 11113500;
+    auto const expectedIntercept = -395335;
+
+    tracker.setPeriod(idealPeriod);
+    for (auto const& timestamp : simulatedVsyncs) {
+        tracker.addVsyncTimestamp(timestamp);
+    }
+
+    auto [slope, intercept] = tracker.getVSyncPredictionModel();
+    EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+    EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+
+    // (timePoint - oldestTS) % expectedPeriod works out to be: 395334
+    // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
+    // so failure to account for the offset will floor the ordinal to 38, which was in the past.
+    auto const timePoint = 158929728723871;
+    auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
+    EXPECT_THAT(prediction, Ge(timePoint));
+}
+
+TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
+    auto const idealPeriod = 10000;
+    auto const realPeriod = 10500;
+    tracker.setPeriod(idealPeriod);
+    for (auto i = 0; i < kMinimumSamplesForPrediction; i++) {
+        tracker.addVsyncTimestamp(i * realPeriod);
+    }
+
+    EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+                IsCloseTo(realPeriod, mMaxRoundingError));
+    tracker.resetModel();
+    EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()),
+                IsCloseTo(idealPeriod, mMaxRoundingError));
+}
+
+TEST_F(VSyncPredictorTest, slopeAlwaysValid) {
+    constexpr auto kNumVsyncs = 100;
+    auto invalidPeriod = mPeriod;
+    auto now = 0;
+    for (int i = 0; i < kNumVsyncs; i++) {
+        tracker.addVsyncTimestamp(now);
+        now += invalidPeriod;
+        invalidPeriod *= 0.9f;
+
+        auto [slope, intercept] = tracker.getVSyncPredictionModel();
+        EXPECT_THAT(slope, IsCloseTo(mPeriod, mPeriod * kOutlierTolerancePercent / 100.f));
+
+        // When VsyncPredictor returns the period it means that it doesn't know how to predict and
+        // it needs to get more samples
+        if (slope == mPeriod && intercept == 0) {
+            EXPECT_TRUE(tracker.needsMoreSamples(now));
+        }
+    }
+}
+
+} // namespace android::scheduler
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
new file mode 100644
index 0000000..ac95938
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -0,0 +1,697 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+#include <array>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+    MockVSyncTracker() { ON_CALL(*this, addVsyncTimestamp(_)).WillByDefault(Return(true)); }
+    MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t));
+    MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+    MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+    MOCK_METHOD1(setPeriod, void(nsecs_t));
+    MOCK_METHOD0(resetModel, void());
+};
+
+class VSyncTrackerWrapper : public VSyncTracker {
+public:
+    VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
+
+    bool addVsyncTimestamp(nsecs_t timestamp) final {
+        return mTracker->addVsyncTimestamp(timestamp);
+    }
+    nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+        return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
+    }
+    nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
+    void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
+    void resetModel() final { mTracker->resetModel(); }
+
+private:
+    std::shared_ptr<VSyncTracker> const mTracker;
+};
+
+class MockClock : public Clock {
+public:
+    MOCK_CONST_METHOD0(now, nsecs_t());
+};
+
+class ClockWrapper : public Clock {
+public:
+    ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
+
+    nsecs_t now() const { return mClock->now(); }
+
+private:
+    std::shared_ptr<Clock> const mClock;
+};
+
+class MockVSyncDispatch : public VSyncDispatch {
+public:
+    MOCK_METHOD2(registerCallback,
+                 CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string));
+    MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+    MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+    MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+};
+
+class VSyncDispatchWrapper : public VSyncDispatch {
+public:
+    VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
+    CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn,
+                                   std::string callbackName) final {
+        return mDispatch->registerCallback(callbackFn, callbackName);
+    }
+
+    void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
+
+    ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+                            nsecs_t earliestVsync) final {
+        return mDispatch->schedule(token, workDuration, earliestVsync);
+    }
+
+    CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+
+private:
+    std::shared_ptr<VSyncDispatch> const mDispatch;
+};
+
+std::shared_ptr<FenceTime> generateInvalidFence() {
+    sp<Fence> fence = new Fence();
+    return std::make_shared<FenceTime>(fence);
+}
+
+std::shared_ptr<FenceTime> generatePendingFence() {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    return std::make_shared<FenceTime>(fence);
+}
+
+void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
+    FenceTime::Snapshot snap(time);
+    fence->applyTrustedSnapshot(snap);
+}
+
+std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+    sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+    std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+    signalFenceWithTime(ft, time);
+    return ft;
+}
+
+class StubCallback : public DispSync::Callback {
+public:
+    void onDispSyncEvent(nsecs_t when) final {
+        std::lock_guard<std::mutex> lk(mMutex);
+        mLastCallTime = when;
+    }
+    std::optional<nsecs_t> lastCallTime() const {
+        std::lock_guard<std::mutex> lk(mMutex);
+        return mLastCallTime;
+    }
+
+private:
+    std::mutex mutable mMutex;
+    std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
+};
+
+class VSyncReactorTest : public testing::Test {
+protected:
+    VSyncReactorTest()
+          : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
+            mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
+            mMockClock(std::make_shared<NiceMock<MockClock>>()),
+            mReactor(std::make_unique<ClockWrapper>(mMockClock),
+                     std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
+                     std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+        ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
+        ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
+    }
+
+    std::shared_ptr<MockVSyncDispatch> mMockDispatch;
+    std::shared_ptr<MockVSyncTracker> mMockTracker;
+    std::shared_ptr<MockClock> mMockClock;
+    static constexpr size_t kPendingLimit = 3;
+    static constexpr nsecs_t mDummyTime = 47;
+    static constexpr nsecs_t mPhase = 3000;
+    static constexpr nsecs_t mAnotherPhase = 5200;
+    static constexpr nsecs_t period = 10000;
+    static constexpr nsecs_t mFakeVSyncTime = 2093;
+    static constexpr nsecs_t mFakeWakeupTime = 1892;
+    static constexpr nsecs_t mFakeNow = 2214;
+    static constexpr const char mName[] = "callbacky";
+    VSyncDispatch::CallbackToken const mFakeToken{2398};
+
+    nsecs_t lastCallbackTime = 0;
+    StubCallback outerCb;
+    std::function<void(nsecs_t, nsecs_t)> innerCb;
+
+    VSyncReactor mReactor;
+};
+
+TEST_F(VSyncReactorTest, addingNullFenceCheck) {
+    EXPECT_FALSE(mReactor.addPresentFence(nullptr));
+}
+
+TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
+    EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
+}
+
+TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
+    nsecs_t anotherDummyTime = 2919019201;
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
+    auto pendingFence = generatePendingFence();
+    EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    signalFenceWithTime(pendingFence, mDummyTime);
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, limitsPendingFences) {
+    std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+    std::array<nsecs_t, fences.size()> fakeTimes;
+    std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
+    std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
+        i++;
+        return i * i;
+    });
+
+    for (auto const& fence : fences) {
+        mReactor.addPresentFence(fence);
+    }
+
+    for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
+        EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
+    }
+
+    for (auto i = 0u; i < fences.size(); i++) {
+        signalFenceWithTime(fences[i], fakeTimes[i]);
+    }
+    mReactor.addPresentFence(generatePendingFence());
+}
+
+TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
+    static constexpr size_t aFewTimes = 8;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1);
+
+    mReactor.setIgnorePresentFences(true);
+    for (auto i = 0; i < aFewTimes; i++) {
+        mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime));
+    }
+
+    mReactor.setIgnorePresentFences(false);
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
+    mReactor.setIgnorePresentFences(true);
+
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+
+    EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
+    nsecs_t const fakeTimestamp = 4839;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(1)
+            .WillOnce(Return(fakeTimestamp));
+
+    EXPECT_THAT(mReactor.computeNextRefresh(0), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
+    nsecs_t const fakeTimestamp = 4839;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+            .Times(1)
+            .WillOnce(Return(fakeTimestamp));
+
+    EXPECT_THAT(mReactor.expectedPresentTime(), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
+    nsecs_t const fakeTimestamp = 4839;
+    nsecs_t const fakePeriod = 1010;
+    nsecs_t const mFakeNow = 2214;
+    int const numPeriodsOut = 3;
+    EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
+    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+    EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
+            .WillOnce(Return(fakeTimestamp));
+    EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, getPeriod) {
+    nsecs_t const fakePeriod = 1010;
+    EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+    EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
+}
+
+TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
+    nsecs_t const newPeriod = 5000;
+    EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+    mReactor.setPeriod(newPeriod);
+
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+
+    EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
+    nsecs_t sampleTime = 0;
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+
+    mReactor.setPeriod(period);
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) {
+    nsecs_t sampleTime = 0;
+    nsecs_t const secondPeriod = 5000;
+    nsecs_t const thirdPeriod = 2000;
+
+    mReactor.setPeriod(secondPeriod);
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    mReactor.setPeriod(thirdPeriod);
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+            .WillOnce(Return(false))
+            .WillOnce(Return(true))
+            .WillOnce(Return(true));
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+    nsecs_t skewyPeriod = period >> 1;
+    bool periodFlushed = false;
+    nsecs_t sampleTime = 0;
+    EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncPendingFence) {
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_))
+            .Times(2)
+            .WillOnce(Return(false))
+            .WillOnce(Return(true));
+
+    auto fence = generatePendingFence();
+    EXPECT_FALSE(mReactor.addPresentFence(fence));
+    signalFenceWithTime(fence, period >> 1);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) {
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) {
+    nsecs_t const newPeriod = 5000;
+    EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0);
+    mReactor.setPeriod(newPeriod);
+
+    bool periodFlushed = true;
+    EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+    EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1);
+    EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, addResyncSampleTypical) {
+    nsecs_t const fakeTimestamp = 3032;
+    bool periodFlushed = false;
+
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+    EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
+    bool periodFlushed = false;
+    nsecs_t const newPeriod = 4000;
+
+    mReactor.setPeriod(newPeriod);
+
+    auto time = 0;
+    auto constexpr numTimestampSubmissions = 10;
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += period;
+        EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_FALSE(periodFlushed);
+    }
+
+    time += newPeriod;
+    EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    for (auto i = 0; i < numTimestampSubmissions; i++) {
+        time += newPeriod;
+        EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed));
+        EXPECT_FALSE(periodFlushed);
+    }
+}
+
+TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
+    auto time = 0;
+    bool periodFlushed = false;
+    nsecs_t const newPeriod = 4000;
+    mReactor.setPeriod(newPeriod);
+
+    time += period;
+    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+
+    time += newPeriod;
+    mReactor.addResyncSample(time, std::nullopt, &periodFlushed);
+
+    EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
+    return period - phase;
+}
+
+TEST_F(VSyncReactorTest, addEventListener) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
+            .Times(2)
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+}
+
+TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime))
+            .InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+    EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime));
+}
+
+TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+// b/149221293
+TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) {
+    class SelfRemovingCallback : public DispSync::Callback {
+    public:
+        SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {}
+        void onDispSyncEvent(nsecs_t when) final { mVsr.removeEventListener(this, &when); }
+
+    private:
+        VSyncReactor& mVsr;
+    } selfRemover(mReactor);
+
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime);
+    innerCb(0, 0);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+    EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) {
+    static constexpr nsecs_t anotherPeriod = 23333;
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+            .InSequence(seq);
+    EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod));
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow))
+            .InSequence(seq);
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+
+    bool periodFlushed = false;
+    mReactor.setPeriod(anotherPeriod);
+    EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed));
+    EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+            .InSequence(seq)
+            .WillOnce(Return(ScheduleResult::Scheduled));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
+    ASSERT_TRUE(innerCb);
+    innerCb(mFakeVSyncTime, mFakeWakeupTime);
+}
+
+TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
+    nsecs_t const negativePhase = -4000;
+    Sequence seq;
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .InSequence(seq)
+            .WillOnce(Return(mFakeToken));
+    EXPECT_CALL(*mMockDispatch,
+                schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
+            .InSequence(seq);
+    mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, beginResyncResetsModel) {
+    EXPECT_CALL(*mMockTracker, resetModel());
+    mReactor.beginResync();
+}
+
+TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
+    bool periodFlushed = true;
+    EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
+    mReactor.setIgnorePresentFences(true);
+
+    nsecs_t const newPeriod = 5000;
+    mReactor.setPeriod(newPeriod);
+
+    EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed));
+    EXPECT_FALSE(periodFlushed);
+    EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed));
+    EXPECT_TRUE(periodFlushed);
+
+    EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+}
+
+using VSyncReactorDeathTest = VSyncReactorTest;
+TEST_F(VSyncReactorDeathTest, invalidRemoval) {
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+    EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, invalidChange) {
+    EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
+
+    // the current DispSync-interface usage pattern has evolved around an implementation quirk,
+    // which is a callback is assumed to always exist, and it is valid api usage to change the
+    // offset of an object that is in the removed state.
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+    mReactor.changePhaseOffset(&outerCb, mPhase);
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
+    ON_CALL(*mMockDispatch, schedule(_, _, _))
+            .WillByDefault(Return(ScheduleResult::CannotSchedule));
+    EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
+    EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+            .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+    EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
+
+    mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+    ASSERT_TRUE(innerCb);
+    Mock::VerifyAndClearExpectations(mMockDispatch.get());
+
+    ON_CALL(*mMockDispatch, schedule(_, _, _))
+            .WillByDefault(Return(ScheduleResult::CannotSchedule));
+    EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*");
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
index 7ed57b9..0780af1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "mock/DisplayHardware/MockComposer.h"
 
 namespace android {
@@ -27,3 +31,6 @@
 } // namespace mock
 } // namespace Hwc2
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 3c7e1da..5cbba81 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -39,9 +39,9 @@
 using android::hardware::graphics::composer::V2_1::Display;
 using android::hardware::graphics::composer::V2_1::Error;
 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_3::IComposerClient;
+using android::hardware::graphics::composer::V2_4::IComposerCallback;
+using android::hardware::graphics::composer::V2_4::IComposerClient;
 
 class Composer : public Hwc2::Composer {
 public:
@@ -71,7 +71,6 @@
     MOCK_METHOD2(getDisplayName, Error(Display, std::string*));
     MOCK_METHOD4(getDisplayRequests,
                  Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*));
-    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_METHOD1(getPerFrameMetadataKeys,
@@ -118,10 +117,28 @@
     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>&));
     MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
+    MOCK_METHOD0(isVsyncPeriodSwitchSupported, bool());
+    MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
+    MOCK_METHOD2(getDisplayConnectionType,
+                 V2_4::Error(Display, IComposerClient::DisplayConnectionType*));
+    MOCK_METHOD3(getSupportedDisplayVsyncPeriods,
+                 V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
+    MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
+    MOCK_METHOD4(setActiveConfigWithConstraints,
+                 V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+                             VsyncPeriodChangeTimeline*));
+    MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
+    MOCK_METHOD2(getSupportedContentTypes,
+                 V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
+    MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
+    MOCK_METHOD5(setLayerGenericMetadata,
+                 V2_4::Error(Display, Layer, const std::string&, bool,
+                             const std::vector<uint8_t>&));
+    MOCK_METHOD1(getLayerGenericMetadataKeys,
+                 V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
index 9ca116d..a2ae6c9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -31,7 +31,7 @@
     MOCK_METHOD0(reset, void());
     MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
     MOCK_METHOD0(beginResync, void());
-    MOCK_METHOD2(addResyncSample, bool(nsecs_t, bool*));
+    MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*));
     MOCK_METHOD0(endResync, void());
     MOCK_METHOD1(setPeriod, void(nsecs_t));
     MOCK_METHOD0(getPeriod, nsecs_t());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ed35ebf..50eb390 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,14 +33,16 @@
     MOCK_METHOD0(onScreenReleased, void());
     MOCK_METHOD0(onScreenAcquired, void());
     MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
-    MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, int32_t));
+    MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t));
     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_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
+    MOCK_METHOD0(requestLatestConfig, void());
     MOCK_METHOD1(pauseVsyncCallback, void(bool));
+    MOCK_METHOD0(getEventThreadConnectionCount, size_t());
 };
 
 } // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
similarity index 75%
copy from services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
copy to services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp
index a7fd667..358dfdb 100644
--- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.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,14 @@
  * limitations under the License.
  */
 
-#include "mock/gui/MockGraphicBufferProducer.h"
+#include "mock/MockFrameTracer.h"
 
 namespace android {
 namespace mock {
 
 // Explicit default instantiation is recommended.
-GraphicBufferProducer::GraphicBufferProducer() = default;
-GraphicBufferProducer::~GraphicBufferProducer() = default;
+FrameTracer::FrameTracer() = default;
+FrameTracer::~FrameTracer() = default;
 
 } // namespace mock
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h
new file mode 100644
index 0000000..f768b81
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.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>
+
+#include "FrameTracer/FrameTracer.h"
+
+namespace android {
+namespace mock {
+
+class FrameTracer : public android::FrameTracer {
+public:
+    FrameTracer();
+    ~FrameTracer();
+
+    MOCK_METHOD0(initialize, void());
+    MOCK_METHOD0(registerDataSource, void());
+    MOCK_METHOD2(traceNewLayer, void(int32_t, const std::string&));
+    MOCK_METHOD6(traceTimestamp,
+                 void(int32_t, uint64_t, uint64_t, nsecs_t, FrameEvent::BufferEventType, nsecs_t));
+    MOCK_METHOD6(traceFence,
+                 void(int32_t, uint64_t, uint64_t, const std::shared_ptr<FenceTime>&,
+                      FrameEvent::BufferEventType, nsecs_t));
+    MOCK_METHOD0(miniDump, std::string());
+};
+
+} // namespace mock
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
new file mode 100644
index 0000000..e2f6abd
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -0,0 +1,37 @@
+/*
+ * 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 "Layer.h"
+
+namespace android::mock {
+
+class MockLayer : public Layer {
+public:
+    explicit MockLayer(SurfaceFlinger* flinger)
+          : Layer(LayerCreationArgs(flinger, nullptr, "TestLayer", 800, 600, 0, {})) {}
+
+    MOCK_CONST_METHOD0(getType, const char*());
+    MOCK_METHOD0(getFrameSelectionPriority, int32_t());
+    MOCK_CONST_METHOD0(isVisible, bool());
+    MOCK_METHOD0(createClone, sp<Layer>());
+    MOCK_CONST_METHOD0(getFrameRate, FrameRate());
+};
+
+} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index 1b1c1a7..e781c0a 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -30,7 +30,6 @@
     ~MessageQueue() override;
 
     MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&));
-    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));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
index 4129328..7e925b9 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include "mock/MockSurfaceInterceptor.h"
 
 namespace android {
@@ -25,3 +29,6 @@
 
 } // namespace mock
 } // namespace android
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index b1634a8..c45d584 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -28,13 +28,21 @@
     TimeStats();
     ~TimeStats() override;
 
+    MOCK_METHOD0(onBootFinished, void());
     MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&));
     MOCK_METHOD0(isEnabled, bool());
     MOCK_METHOD0(miniDump, std::string());
     MOCK_METHOD0(incrementTotalFrames, void());
     MOCK_METHOD0(incrementMissedFrames, void());
     MOCK_METHOD0(incrementClientCompositionFrames, void());
+    MOCK_METHOD0(incrementClientCompositionReusedFrames, void());
+    MOCK_METHOD1(recordDisplayEventConnectionCount, void(int32_t));
+    MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
+    MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
+    MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
     MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
+    MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
+    MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
     MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
     MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h
new file mode 100644
index 0000000..1318deb
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/CallbackUtils.h
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+#include <ui/Fence.h>
+#include <utils/Timers.h>
+#include <thread>
+
+namespace android {
+
+namespace {
+
+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, latch, acquireTime, presentFence, previousReleaseFence,
+                     transformHint,
+                     frameEvents] = 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;
+};
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
new file mode 100644
index 0000000..07916b6
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -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.
+ */
+#pragma once
+
+#include <ui/ColorSpace.h>
+
+namespace android {
+
+namespace {
+
+struct Color {
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+
+    static const Color RED;
+    static const Color GREEN;
+    static const Color BLUE;
+    static const Color WHITE;
+    static const Color BLACK;
+    static const Color TRANSPARENT;
+};
+
+const Color Color::RED{255, 0, 0, 255};
+const Color Color::GREEN{0, 255, 0, 255};
+const Color Color::BLUE{0, 0, 255, 255};
+const Color Color::WHITE{255, 255, 255, 255};
+const Color Color::BLACK{0, 0, 0, 255};
+const Color Color::TRANSPARENT{0, 0, 0, 0};
+
+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);
+    }
+
+    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;
+    }
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
new file mode 100644
index 0000000..5480b00
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <ui/Rect.h>
+#include <utils/String8.h>
+#include "TransactionUtils.h"
+
+namespace android {
+
+namespace {
+
+// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
+// individual pixel values for testing purposes.
+class ScreenCapture : public RefBase {
+public:
+    static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+        captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+    }
+
+    static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
+        const auto sf = ComposerService::getComposerService();
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+                              Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+                                   Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) {
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    static void captureChildLayersExcluding(
+            std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle,
+            std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) {
+        sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+        SurfaceComposerClient::Transaction().apply(true);
+
+        sp<GraphicBuffer> outBuffer;
+        ASSERT_EQ(NO_ERROR,
+                  sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB,
+                                    ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers,
+                                    1.0f, true));
+        *sc = std::make_unique<ScreenCapture>(outBuffer);
+    }
+
+    void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+        TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
+    }
+
+    void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
+        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+        const bool leftBorder = rect.left > 0;
+        const bool topBorder = rect.top > 0;
+        const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth());
+        const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight());
+
+        if (topBorder) {
+            Rect top(rect.left, rect.top - 1, rect.right, rect.top);
+            if (leftBorder) {
+                top.left -= 1;
+            }
+            if (rightBorder) {
+                top.right += 1;
+            }
+            expectColor(top, color, tolerance);
+        }
+        if (leftBorder) {
+            Rect left(rect.left - 1, rect.top, rect.left, rect.bottom);
+            expectColor(left, color, tolerance);
+        }
+        if (rightBorder) {
+            Rect right(rect.right, rect.top, rect.right + 1, rect.bottom);
+            expectColor(right, color, tolerance);
+        }
+        if (bottomBorder) {
+            Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1);
+            if (leftBorder) {
+                bottom.left -= 1;
+            }
+            if (rightBorder) {
+                bottom.right += 1;
+            }
+            expectColor(bottom, color, tolerance);
+        }
+    }
+
+    void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight,
+                        const Color& bottomLeft, const Color& bottomRight, bool filtered = false,
+                        uint8_t tolerance = 0) {
+        ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0);
+
+        const int32_t centerX = rect.left + (rect.right - rect.left) / 2;
+        const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2;
+        // avoid checking borders due to unspecified filtering behavior
+        const int32_t offsetX = filtered ? 2 : 0;
+        const int32_t offsetY = filtered ? 2 : 0;
+        expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft,
+                    tolerance);
+        expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight,
+                    tolerance);
+        expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft,
+                    tolerance);
+        expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom),
+                    bottomRight, tolerance);
+    }
+
+    void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+        ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
+        const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
+        if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
+            String8 err(String8::format("pixel @ (%3d, %3d): "
+                                        "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
+                                        x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
+            EXPECT_EQ(String8(), err) << err.string();
+        }
+    }
+
+    void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
+
+    void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
+
+    void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
+
+    explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
+        mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
+    }
+
+    ~ScreenCapture() { mOutBuffer->unlock(); }
+
+private:
+    sp<GraphicBuffer> mOutBuffer;
+    uint8_t* mPixels = nullptr;
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
new file mode 100644
index 0000000..2c63da0
--- /dev/null
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <gtest/gtest.h>
+
+#include <android/native_window.h>
+#include <hardware/hwcomposer_defs.h>
+
+#include <binder/IPCThreadState.h>
+
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+
+#include <private/gui/ComposerService.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+
+#include "ColorUtils.h"
+
+namespace android {
+
+namespace {
+
+using namespace std::chrono_literals;
+using Transaction = SurfaceComposerClient::Transaction;
+
+std::ostream& operator<<(std::ostream& os, const Color& color) {
+    os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a);
+    return os;
+}
+
+class TransactionUtils {
+public:
+    // Fill a region with the specified color.
+    static 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;
+        }
+
+        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;
+                dst[3] = color.a;
+                dst += 4;
+            }
+        }
+    }
+
+    // Fill a region with the specified color.
+    static 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.
+    static void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels,
+                                  const Rect& rect, const Color& color, uint8_t tolerance) {
+        int32_t x = rect.left;
+        int32_t y = rect.top;
+        int32_t width = rect.right - rect.left;
+        int32_t height = rect.bottom - rect.top;
+
+        int32_t bufferWidth = int32_t(outBuffer->getWidth());
+        int32_t bufferHeight = int32_t(outBuffer->getHeight());
+        if (x + width > bufferWidth) {
+            x = std::min(x, bufferWidth);
+            width = bufferWidth - x;
+        }
+        if (y + height > bufferHeight) {
+            y = std::min(y, bufferHeight);
+            height = bufferHeight - y;
+        }
+
+        auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
+            uint8_t tmp = a >= b ? a - b : b - a;
+            return tmp <= tolerance;
+        };
+        for (int32_t j = 0; j < height; j++) {
+            const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4;
+            for (int32_t i = 0; i < width; i++) {
+                const uint8_t expected[4] = {color.r, color.g, color.b, color.a};
+                EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare))
+                        << "pixel @ (" << x + i << ", " << y + j << "): "
+                        << "expected (" << color << "), "
+                        << "got (" << Color{src[0], src[1], src[2], src[3]} << ")";
+                src += 4;
+            }
+        }
+    }
+
+    static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const Color& color,
+                                 bool unlock = true) {
+        fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock);
+    }
+
+    // Fill an RGBA_8888 formatted surface with a single color.
+    static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
+                                 bool unlock = true) {
+        ANativeWindow_Buffer outBuffer;
+        sp<Surface> s = sc->getSurface();
+        ASSERT_TRUE(s != nullptr);
+        ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr));
+        uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits);
+        for (int y = 0; y < outBuffer.height; y++) {
+            for (int x = 0; x < outBuffer.width; x++) {
+                uint8_t* pixel = img + (4 * (y * outBuffer.stride + x));
+                pixel[0] = r;
+                pixel[1] = g;
+                pixel[2] = b;
+                pixel[3] = 255;
+            }
+        }
+        if (unlock) {
+            ASSERT_EQ(NO_ERROR, s->unlockAndPost());
+        }
+    }
+};
+
+enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY };
+
+// 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(); }
+};
+
+/** RAII Wrapper around get/seteuid */
+class UIDFaker {
+    uid_t oldId;
+
+public:
+    UIDFaker(uid_t uid) {
+        oldId = geteuid();
+        seteuid(uid);
+    }
+    ~UIDFaker() { seteuid(oldId); }
+};
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/vsync/vsync.cpp b/services/surfaceflinger/tests/vsync/vsync.cpp
index a1b45e6..667dfb9 100644
--- a/services/surfaceflinger/tests/vsync/vsync.cpp
+++ b/services/surfaceflinger/tests/vsync/vsync.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+
 #include <android/looper.h>
 #include <gui/DisplayEventReceiver.h>
 #include <utils/Looper.h>
@@ -82,3 +86,6 @@
 
     return 0;
 }
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp
index a7fd912..b71964b 100644
--- a/services/vr/bufferhubd/producer_channel.cpp
+++ b/services/vr/bufferhubd/producer_channel.cpp
@@ -350,7 +350,7 @@
     while (!buffer_state_->compare_exchange_weak(
         current_buffer_state, updated_buffer_state, std::memory_order_acq_rel,
         std::memory_order_acquire)) {
-      ALOGI(
+      ALOGV(
           "%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 "
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index c202b5c..4df7b7c 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -12,10 +12,14 @@
     ],
 
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
+        "android.hardware.graphics.composer@2.1-resources",
         "android.hardware.graphics.mapper@2.0",
         "android.hardware.graphics.mapper@3.0",
+        "android.hardware.graphics.mapper@4.0",
         "libbase",
         "libbufferhubqueue",
         "libbinder",
@@ -32,11 +36,11 @@
 
     header_libs: [
         "android.hardware.graphics.composer@2.1-command-buffer",
-        "android.hardware.graphics.composer@2.1-hal",
+        "android.hardware.graphics.composer@2.3-hal",
     ],
 
     export_header_lib_headers: [
-        "android.hardware.graphics.composer@2.1-hal",
+        "android.hardware.graphics.composer@2.3-hal",
     ],
 
     export_static_lib_headers: [
@@ -44,8 +48,10 @@
     ],
 
     export_shared_lib_headers: [
-        "android.frameworks.vr.composer@1.0",
+        "android.frameworks.vr.composer@2.0",
         "android.hardware.graphics.composer@2.1",
+        "android.hardware.graphics.composer@2.2",
+        "android.hardware.graphics.composer@2.3",
     ],
 
     export_include_dirs: ["."],
@@ -102,8 +108,8 @@
         "libvr_hwc-binder",
     ],
     shared_libs: [
-        "android.frameworks.vr.composer@1.0",
-        "android.hardware.graphics.composer@2.1",
+        "android.frameworks.vr.composer@2.0",
+        "android.hardware.graphics.composer@2.3",
         "libbase",
         "libbinder",
         "liblog",
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp
index 786d5fa..dd1603d 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.cpp
+++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
+#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
 #include <hardware/gralloc.h>
 #include <hardware/gralloc1.h>
 #include <log/log.h>
@@ -27,8 +27,7 @@
 namespace android {
 namespace dvr {
 
-using android::hardware::graphics::common::V1_0::PixelFormat;
-using android::frameworks::vr::composer::V1_0::IVrComposerClient;
+using android::frameworks::vr::composer::V2_0::IVrComposerClient;
 
 VrComposerClient::VrComposerClient(dvr::VrHwc& hal)
     : ComposerClient(&hal), mVrHal(hal) {
@@ -51,7 +50,8 @@
 VrComposerClient::VrCommandEngine::~VrCommandEngine() {}
 
 bool VrComposerClient::VrCommandEngine::executeCommand(
-    IComposerClient::Command command, uint16_t length) {
+    hardware::graphics::composer::V2_1::IComposerClient::Command command,
+    uint16_t length) {
   IVrComposerClient::VrCommand vrCommand =
       static_cast<IVrComposerClient::VrCommand>(command);
   switch (vrCommand) {
@@ -73,7 +73,7 @@
 
   auto err = mVrHal.setLayerInfo(mCurrentDisplay, mCurrentLayer, read(), read());
   if (err != Error::NONE) {
-    mWriter.setError(getCommandLoc(), err);
+    mWriter->setError(getCommandLoc(), err);
   }
 
   return true;
@@ -86,7 +86,7 @@
 
   auto err = mVrHal.setClientTargetMetadata(mCurrentDisplay, readBufferMetadata());
   if (err != Error::NONE)
-    mWriter.setError(getCommandLoc(), err);
+    mWriter->setError(getCommandLoc(), err);
 
   return true;
 }
@@ -99,7 +99,7 @@
   auto err = mVrHal.setLayerBufferMetadata(mCurrentDisplay, mCurrentLayer,
                                            readBufferMetadata());
   if (err != Error::NONE)
-    mWriter.setError(getCommandLoc(), err);
+    mWriter->setError(getCommandLoc(), err);
 
   return true;
 }
@@ -107,12 +107,14 @@
 IVrComposerClient::BufferMetadata
 VrComposerClient::VrCommandEngine::readBufferMetadata() {
   IVrComposerClient::BufferMetadata metadata = {
-    .width = read(),
-    .height = read(),
-    .stride = read(),
-    .layerCount = read(),
-    .format = static_cast<PixelFormat>(readSigned()),
-    .usage = read64(),
+      .width = read(),
+      .height = read(),
+      .stride = read(),
+      .layerCount = read(),
+      .format =
+          static_cast<android::hardware::graphics::common::V1_2::PixelFormat>(
+              readSigned()),
+      .usage = read64(),
   };
   return metadata;
 }
diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h
index 0b7ce5e..1b2b5f4 100644
--- a/services/vr/hardware_composer/impl/vr_composer_client.h
+++ b/services/vr/hardware_composer/impl/vr_composer_client.h
@@ -17,10 +17,12 @@
 #ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
 #define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H
 
-#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
-#include <composer-command-buffer/2.1/ComposerCommandBuffer.h>
+#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
+#include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
 #include <composer-hal/2.1/ComposerClient.h>
 #include <composer-hal/2.1/ComposerCommandEngine.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
 
 namespace android {
 namespace dvr {
@@ -28,8 +30,8 @@
 class VrHwc;
 
 using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine;
-using hardware::graphics::composer::V2_1::hal::ComposerHal;
-using hardware::graphics::composer::V2_1::hal::detail::ComposerClientImpl;
+using hardware::graphics::composer::V2_3::hal::ComposerHal;
+using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl;
 
 using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>;
 
@@ -44,8 +46,9 @@
     explicit VrCommandEngine(VrComposerClient& client);
     ~VrCommandEngine() override;
 
-    bool executeCommand(IComposerClient::Command command,
-                        uint16_t length) override;
+    bool executeCommand(
+        hardware::graphics::composer::V2_1::IComposerClient::Command command,
+        uint16_t length) override;
 
    private:
     bool executeSetLayerInfo(uint16_t length);
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index fb7932d..e530b16 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -27,7 +27,7 @@
 #include "vr_composer_client.h"
 
 using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_3;
 
 using android::base::StringPrintf;
 using android::hardware::hidl_handle;
@@ -36,12 +36,12 @@
 using android::hardware::Return;
 using android::hardware::Void;
 
+namespace types = android::hardware::graphics::common;
+
 namespace android {
 namespace dvr {
 namespace {
 
-using android::hardware::graphics::common::V1_0::PixelFormat;
-
 const Display kDefaultDisplayId = 1;
 const Config kDefaultConfigId = 1;
 
@@ -269,7 +269,8 @@
   // onHotplug() call, so it's important to release mutex_ here.
   lock.unlock();
   event_callback_->onHotplug(kDefaultDisplayId,
-                             IComposerCallback::Connection::CONNECTED);
+                             hardware::graphics::composer::V2_1::
+                                 IComposerCallback::Connection::CONNECTED);
   lock.lock();
   UpdateVsyncCallbackEnabledLocked();
 }
@@ -282,15 +283,6 @@
 
 uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
 
-Error VrHwc::createVirtualDisplay(uint32_t width, uint32_t height,
-                                  PixelFormat* format, Display* outDisplay) {
-  *format = PixelFormat::RGBA_8888;
-  *outDisplay = display_count_;
-  displays_[display_count_].reset(new HwcDisplay(width, height));
-  display_count_++;
-  return Error::NONE;
-}
-
 Error VrHwc::destroyVirtualDisplay(Display display) {
   std::lock_guard<std::mutex> guard(mutex_);
   if (display == kDefaultDisplayId || displays_.erase(display) == 0)
@@ -332,24 +324,6 @@
   return Error::NONE;
 }
 
-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;
-}
-
-Error VrHwc::getColorModes(Display /* display */,
-                           hidl_vec<ColorMode>* outModes) {
-  std::vector<ColorMode> color_modes(1, ColorMode::NATIVE);
-  *outModes = hidl_vec<ColorMode>(color_modes);
-  return Error::NONE;
-}
-
 Error VrHwc::getDisplayAttribute(Display display, Config config,
                                  IComposerClient::Attribute attribute,
                                  int32_t* outValue) {
@@ -441,17 +415,6 @@
   return Error::NONE;
 }
 
-Error VrHwc::getHdrCapabilities(Display /* display */,
-                                hidl_vec<Hdr>* /* outTypes */,
-                                float* outMaxLuminance,
-                                float* outMaxAverageLuminance,
-                                float* outMinLuminance) {
-  *outMaxLuminance = 0;
-  *outMaxAverageLuminance = 0;
-  *outMinLuminance = 0;
-  return Error::NONE;
-}
-
 Error VrHwc::setActiveConfig(Display display, Config config) {
   std::lock_guard<std::mutex> guard(mutex_);
   auto display_ptr = FindDisplay(display);
@@ -464,47 +427,6 @@
   return Error::NONE;
 }
 
-Error VrHwc::setColorMode(Display display, ColorMode mode) {
-  std::lock_guard<std::mutex> guard(mutex_);
-  auto display_ptr = FindDisplay(display);
-  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;
-}
-
 Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) {
   std::lock_guard<std::mutex> guard(mutex_);
   auto display_ptr = FindDisplay(display);
@@ -956,6 +878,23 @@
   return Void();
 }
 
+Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) {
+  std::lock_guard<std::mutex> guard(mutex_);
+
+  Error status = Error::NONE;
+  sp<VrComposerClient> client;
+  if (!client_.promote().get()) {
+    client = new VrComposerClient(*this);
+  } else {
+    ALOGE("Already have a client");
+    status = Error::NO_RESOURCES;
+  }
+
+  client_ = client;
+  hidl_cb(status, client);
+  return Void();
+}
+
 void VrHwc::ForceDisplaysRefresh() {
   std::lock_guard<std::mutex> guard(mutex_);
   if (event_callback_ != nullptr) {
@@ -994,6 +933,26 @@
   vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
 }
 
+Return<void> VrHwc::debug(const hidl_handle& fd,
+                          const hidl_vec<hidl_string>& args) {
+  std::string result;
+
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+    for (const auto& pair : displays_) {
+      result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first));
+      pair.second->dumpDebugInfo(&result);
+    }
+    result += "\n";
+  }
+
+  FILE* out = fdopen(dup(fd->data[0]), "w");
+  fprintf(out, "%s", result.c_str());
+  fclose(out);
+
+  return Void();
+}
+
 void HwcLayer::dumpDebugInfo(std::string* result) const {
   if (!result) {
     return;
@@ -1024,5 +983,196 @@
   callback_ = callback;
 }
 
+// composer::V2_2::ComposerHal
+Error VrHwc::setReadbackBuffer(Display display,
+                               const native_handle_t* bufferHandle,
+                               android::base::unique_fd fenceFd) {
+  return Error::NONE;
+}
+
+Error VrHwc::getReadbackBufferFence(Display display,
+                                    android::base::unique_fd* outFenceFd) {
+  return Error::NONE;
+}
+
+Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+                                      types::V1_1::PixelFormat* format,
+                                      Display* outDisplay) {
+  *format = types::V1_1::PixelFormat::RGBA_8888;
+  *outDisplay = display_count_;
+  displays_[display_count_].reset(new HwcDisplay(width, height));
+  display_count_++;
+  return Error::NONE;
+}
+
+Error VrHwc::setPowerMode_2_2(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;
+}
+
+Error VrHwc::setLayerFloatColor(Display display, Layer layer,
+                                IComposerClient::FloatColor color) {
+  return Error::NONE;
+}
+
+Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode,
+                              std::vector<RenderIntent>* outIntents) {
+  return Error::NONE;
+}
+
+std::array<float, 16> VrHwc::getDataspaceSaturationMatrix(
+    types::V1_1::Dataspace dataspace) {
+  return {};
+}
+
+// composer::V2_3::ComposerHal
+Error VrHwc::getHdrCapabilities_2_3(Display /*display*/,
+                                    hidl_vec<Hdr>* /*outTypes*/,
+                                    float* outMaxLuminance,
+                                    float* outMaxAverageLuminance,
+                                    float* outMinLuminance) {
+  *outMaxLuminance = 0;
+  *outMaxAverageLuminance = 0;
+  *outMinLuminance = 0;
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerPerFrameMetadata_2_3(
+    Display display, Layer layer,
+    const std::vector<IComposerClient::PerFrameMetadata>& metadata) {
+  return Error::NONE;
+}
+
+Error VrHwc::getPerFrameMetadataKeys_2_3(
+    Display display,
+    std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) {
+  return Error::NONE;
+}
+
+Error VrHwc::setColorMode_2_3(Display display, ColorMode mode,
+                              RenderIntent intent) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  auto display_ptr = FindDisplay(display);
+  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::getRenderIntents_2_3(Display display, ColorMode mode,
+                                  std::vector<RenderIntent>* outIntents) {
+  return Error::NONE;
+}
+
+Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) {
+  return Error::NONE;
+}
+
+Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width,
+                                        uint32_t height, PixelFormat format,
+                                        Dataspace dataspace) {
+  return Error::NONE;
+}
+
+Error VrHwc::getReadbackBufferAttributes_2_3(Display display,
+                                             PixelFormat* outFormat,
+                                             Dataspace* outDataspace) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort,
+                                          std::vector<uint8_t>* outData) {
+  int error = 0;
+  auto display_client = display::DisplayClient::Create(&error);
+  if (!display_client) {
+    ALOGE("Could not connect to display service : %s(%d)", strerror(error),
+          error);
+    return Error::BAD_CONFIG;
+  }
+  auto edid_data = display_client->GetConfigurationData(
+      display::ConfigFileType::kDeviceEdid);
+  auto display_identification_port =
+      display_client->GetDisplayIdentificationPort();
+  *outPort = display_identification_port.get();
+
+  std::copy(edid_data.get().begin(), edid_data.get().end(),
+            std::back_inserter(*outData));
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerColorTransform(Display display, Layer layer,
+                                    const float* matrix) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayedContentSamplingAttributes(
+    Display display, PixelFormat& format, Dataspace& dataspace,
+    hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) {
+  return Error::NONE;
+}
+
+Error VrHwc::setDisplayedContentSamplingEnabled(
+    Display display, IComposerClient::DisplayedContentSampling enable,
+    hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
+    uint64_t maxFrames) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames,
+                                       uint64_t timestamp, uint64_t& frameCount,
+                                       hidl_vec<uint64_t>& sampleComponent0,
+                                       hidl_vec<uint64_t>& sampleComponent1,
+                                       hidl_vec<uint64_t>& sampleComponent2,
+                                       hidl_vec<uint64_t>& sampleComponent3) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayCapabilities(
+    Display display,
+    std::vector<IComposerClient::DisplayCapability>* outCapabilities) {
+  return Error::NONE;
+}
+
+Error VrHwc::setLayerPerFrameMetadataBlobs(
+    Display display, Layer layer,
+    std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) {
+  return Error::NONE;
+}
+
+Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) {
+  return Error::NONE;
+}
+
+Error VrHwc::setDisplayBrightness(Display display, float brightness) {
+  return Error::NONE;
+}
+
 }  // 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 e8c0212..3e3a630 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -17,9 +17,9 @@
 #define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H
 
 #include <android-base/unique_fd.h>
-#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 <android/frameworks/vr/composer/2.0/IVrComposerClient.h>
+#include <android/hardware/graphics/composer/2.3/IComposer.h>
+#include <composer-hal/2.3/ComposerHal.h>
 #include <private/dvr/vsync_service.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
@@ -28,15 +28,21 @@
 #include <mutex>
 #include <unordered_map>
 
-using namespace android::frameworks::vr::composer::V1_0;
+using namespace android::frameworks::vr::composer::V2_0;
 using namespace android::hardware::graphics::common::V1_0;
-using namespace android::hardware::graphics::composer::V2_1;
+using namespace android::hardware::graphics::composer::V2_3;
 
+using android::hardware::hidl_bitfield;
 using android::hardware::hidl_handle;
 using android::hardware::hidl_string;
 using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::hardware::Void;
+using android::hardware::graphics::composer::V2_1::Config;
+using android::hardware::graphics::composer::V2_1::Display;
+using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_3::IComposerClient;
 
 namespace android {
 
@@ -46,16 +52,23 @@
 
 class VrComposerClient;
 
-using android::hardware::graphics::common::V1_0::PixelFormat;
-using android::hardware::graphics::composer::V2_1::hal::ComposerHal;
+using android::hardware::graphics::composer::V2_3::hal::ComposerHal;
+
+namespace types = android::hardware::graphics::common;
+
+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;
 
 class ComposerView {
  public:
   struct ComposerLayer {
-    using Recti = hardware::graphics::composer::V2_1::IComposerClient::Rect;
-    using Rectf = hardware::graphics::composer::V2_1::IComposerClient::FRect;
+    using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect;
+    using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect;
     using BlendMode =
-        hardware::graphics::composer::V2_1::IComposerClient::BlendMode;
+        hardware::graphics::composer::V2_3::IComposerClient::BlendMode;
 
     Layer id;
     sp<GraphicBuffer> buffer;
@@ -111,7 +124,7 @@
 
 struct HwcLayer {
   using Composition =
-      hardware::graphics::composer::V2_1::IComposerClient::Composition;
+      hardware::graphics::composer::V2_3::IComposerClient::Composition;
 
   explicit HwcLayer(Layer new_id) { info.id = new_id; }
 
@@ -205,96 +218,157 @@
       Display display, Layer layer,
       const IVrComposerClient::BufferMetadata& metadata);
 
-  // ComposerHal
+  // composer::V2_1::ComposerHal
   bool hasCapability(hwc2_capability_t capability) override;
 
   std::string dumpDebugInfo() override { return {}; }
-  void registerEventCallback(EventCallback* callback) override;
+
+  void registerEventCallback(ComposerHal::EventCallback* callback) override;
   void unregisterEventCallback() override;
 
   uint32_t getMaxVirtualDisplayCount() override;
-  Error createVirtualDisplay(uint32_t width, uint32_t height,
-      PixelFormat* format, Display* outDisplay) override;
   Error destroyVirtualDisplay(Display display) override;
 
   Error createLayer(Display display, Layer* outLayer) override;
   Error destroyLayer(Display display, Layer layer) override;
 
   Error getActiveConfig(Display display, Config* outConfig) override;
-  Error getClientTargetSupport(Display display,
-          uint32_t width, uint32_t height,
-          PixelFormat format, Dataspace dataspace) override;
-  Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
   Error getDisplayAttribute(Display display, Config config,
-          IComposerClient::Attribute attribute, int32_t* outValue) override;
+                            IComposerClient::Attribute attribute,
+                            int32_t* outValue) override;
   Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
   Error getDisplayName(Display display, hidl_string* outName) override;
   Error getDisplayType(Display display,
-          IComposerClient::DisplayType* outType) override;
+                       IComposerClient::DisplayType* outType) override;
   Error getDozeSupport(Display display, bool* outSupport) override;
-  Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes,
-          float* outMaxLuminance, float* outMaxAverageLuminance,
-          float* outMinLuminance) override;
 
   Error setActiveConfig(Display display, Config config) override;
-  Error setColorMode(Display display, ColorMode mode) override;
-  Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
   Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
 
   Error setColorTransform(Display display, const float* matrix,
-          int32_t hint) override;
+                          int32_t hint) override;
   Error setClientTarget(Display display, buffer_handle_t target,
-          int32_t acquireFence, int32_t dataspace,
-          const std::vector<hwc_rect_t>& damage) override;
+                        int32_t acquireFence, int32_t dataspace,
+                        const std::vector<hwc_rect_t>& damage) override;
   Error setOutputBuffer(Display display, buffer_handle_t buffer,
-          int32_t releaseFence) override;
-  Error validateDisplay(Display display,
-          std::vector<Layer>* outChangedLayers,
-          std::vector<IComposerClient::Composition>* outCompositionTypes,
-          uint32_t* outDisplayRequestMask,
-          std::vector<Layer>* outRequestedLayers,
-          std::vector<uint32_t>* outRequestMasks) override;
+                        int32_t releaseFence) override;
+  Error validateDisplay(
+      Display display, std::vector<Layer>* outChangedLayers,
+      std::vector<IComposerClient::Composition>* outCompositionTypes,
+      uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
+      std::vector<uint32_t>* outRequestMasks) override;
   Error acceptDisplayChanges(Display display) override;
   Error presentDisplay(Display display, int32_t* outPresentFence,
-          std::vector<Layer>* outLayers,
-          std::vector<int32_t>* outReleaseFences) override;
+                       std::vector<Layer>* outLayers,
+                       std::vector<int32_t>* outReleaseFences) override;
 
-  Error setLayerCursorPosition(Display display, Layer layer,
-          int32_t x, int32_t y) override;
-  Error setLayerBuffer(Display display, Layer layer,
-          buffer_handle_t buffer, int32_t acquireFence) override;
+  Error setLayerCursorPosition(Display display, Layer layer, int32_t x,
+                               int32_t y) override;
+  Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+                       int32_t acquireFence) override;
   Error setLayerSurfaceDamage(Display display, Layer layer,
-          const std::vector<hwc_rect_t>& damage) override;
+                              const std::vector<hwc_rect_t>& damage) override;
   Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
   Error setLayerColor(Display display, Layer layer,
-          IComposerClient::Color color) override;
+                      IComposerClient::Color color) override;
   Error setLayerCompositionType(Display display, Layer layer,
-          int32_t type) override;
+                                int32_t type) override;
   Error setLayerDataspace(Display display, Layer layer,
-          int32_t dataspace) override;
+                          int32_t dataspace) override;
   Error setLayerDisplayFrame(Display display, Layer layer,
-          const hwc_rect_t& frame) override;
+                             const hwc_rect_t& frame) override;
   Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
   Error setLayerSidebandStream(Display display, Layer layer,
-          buffer_handle_t stream) override;
+                               buffer_handle_t stream) override;
   Error setLayerSourceCrop(Display display, Layer layer,
-          const hwc_frect_t& crop) override;
+                           const hwc_frect_t& crop) override;
   Error setLayerTransform(Display display, Layer layer,
-          int32_t transform) override;
+                          int32_t transform) override;
   Error setLayerVisibleRegion(Display display, Layer layer,
-          const std::vector<hwc_rect_t>& visible) override;
+                              const std::vector<hwc_rect_t>& visible) override;
   Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
 
+  // composer::V2_2::ComposerHal
+  Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
+                          android::base::unique_fd fenceFd) override;
+  Error getReadbackBufferFence(Display display,
+                               android::base::unique_fd* outFenceFd) override;
+  Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+                                 types::V1_1::PixelFormat* format,
+                                 Display* outDisplay) override;
+  Error setPowerMode_2_2(Display display,
+                         IComposerClient::PowerMode mode) override;
+  Error setLayerFloatColor(Display display, Layer layer,
+                           IComposerClient::FloatColor color) override;
+  Error getRenderIntents(Display display, types::V1_1::ColorMode mode,
+                         std::vector<RenderIntent>* outIntents) override;
+  std::array<float, 16> getDataspaceSaturationMatrix(
+      types::V1_1::Dataspace dataspace) override;
+
+  // composer::V2_3::ComposerHal
+  Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes,
+                               float* outMaxLuminance,
+                               float* outMaxAverageLuminance,
+                               float* outMinLuminance) override;
+  Error setLayerPerFrameMetadata_2_3(
+      Display display, Layer layer,
+      const std::vector<IComposerClient::PerFrameMetadata>& metadata) override;
+  Error getPerFrameMetadataKeys_2_3(
+      Display display,
+      std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override;
+  Error setColorMode_2_3(Display display, ColorMode mode,
+                         RenderIntent intent) override;
+  Error getRenderIntents_2_3(Display display, ColorMode mode,
+                             std::vector<RenderIntent>* outIntents) override;
+  Error getColorModes_2_3(Display display,
+                          hidl_vec<ColorMode>* outModes) override;
+  Error getClientTargetSupport_2_3(Display display, uint32_t width,
+                                   uint32_t height, PixelFormat format,
+                                   Dataspace dataspace) override;
+  Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat,
+                                        Dataspace* outDataspace) override;
+  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& format, Dataspace& dataspace,
+      hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask)
+      override;
+  Error setDisplayedContentSamplingEnabled(
+      Display display, IComposerClient::DisplayedContentSampling enable,
+      hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
+      uint64_t maxFrames) override;
+  Error getDisplayedContentSample(
+      Display display, uint64_t maxFrames, uint64_t timestamp,
+      uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
+      hidl_vec<uint64_t>& sampleComponent1,
+      hidl_vec<uint64_t>& sampleComponent2,
+      hidl_vec<uint64_t>& sampleComponent3) override;
+  Error getDisplayCapabilities(Display display,
+                               std::vector<IComposerClient::DisplayCapability>*
+                                   outCapabilities) override;
+  Error setLayerPerFrameMetadataBlobs(
+      Display display, Layer layer,
+      std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override;
+  Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
+  Error setDisplayBrightness(Display display, float brightness) override;
+
   // IComposer:
   Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
   Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
   Return<void> createClient(createClient_cb hidl_cb) override;
+  Return<void> createClient_2_3(
+      IComposer::createClient_2_3_cb hidl_cb) override;
 
   // ComposerView:
   void ForceDisplaysRefresh() override;
   void RegisterObserver(Observer* observer) override;
   void UnregisterObserver(Observer* observer) override;
 
+  Return<void> debug(const hidl_handle& fd,
+                     const hidl_vec<hidl_string>& args) override;
+
  private:
   class VsyncCallback : public BnVsyncCallback {
    public:
diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc
index 99315ef..0de0f9e 100644
--- a/services/vr/virtual_touchpad/virtual_touchpad.rc
+++ b/services/vr/virtual_touchpad/virtual_touchpad.rc
@@ -1,5 +1,5 @@
 service virtual_touchpad /system/bin/virtual_touchpad
   class core
   user system
-  group system input
+  group system input uhid
   writepid /dev/cpuset/system/tasks
diff --git a/vulkan/Android.bp b/vulkan/Android.bp
index 7747734..4934970 100644
--- a/vulkan/Android.bp
+++ b/vulkan/Android.bp
@@ -31,6 +31,5 @@
 subdirs = [
     "nulldrv",
     "libvulkan",
-    "tools",
     "vkjson",
 ]
diff --git a/vulkan/README.md b/vulkan/README.md
index 9fba728..185aa39 100644
--- a/vulkan/README.md
+++ b/vulkan/README.md
@@ -2,6 +2,10 @@
 
 This subdirectory contains Android's Vulkan loader, as well as some Vulkan-related tools useful to platform developers.
 
+## Documentation
+
+The former contents of doc/implementors_guide/ are now at https://source.android.com/devices/graphics/implement-vulkan.
+
 ## Coding Style
 
 We follow the [Chromium coding style](https://www.chromium.org/developers/coding-style) for naming and formatting, except with four-space indentation instead of two spaces. In general, any C++ features supported by the prebuilt platform toolchain are allowed.
@@ -10,19 +14,9 @@
 
 ## Code Generation
 
-We generate several parts of the loader and tools from a Vulkan API description file, stored in `api/vulkan.api`. Code generation must be done manually because the generator tools aren't part of the platform toolchain (yet?). Files named `foo_gen.*` are generated from the API file and a template file named `foo.tmpl`.
+We generate several parts of the loader and tools driectly from the Vulkan Registry (external/vulkan-headers/registry/vk.xml). Code generation must be done manually because the generator is not part of the platform toolchain (yet?). Files named `foo_gen.*` are generated by the code generator.
 
- To run the generator:
+### Run The Code Generator
 
-### One-time setup
-- Install [golang](https://golang.org/), if you don't have it already.
-- Create a directory (e.g. `$HOME/lib/go`) for local go sources and binaries and add it to `$GOPATH`.
-- `$ git clone https://android.googlesource.com/platform/tools/gpu $GOPATH/src/android.googlesource.com/platform/tools/gpu`
-- `$ go get android.googlesource.com/platform/tools/gpu/api/...`
-- You should now have `$GOPATH/bin/apic`. You might want to add `$GOPATH/bin` to your `$PATH`.
-
-### Generating code
-To generate `libvulkan/*_gen.*`,
-- `$ cd libvulkan`
-- `$ apic template ../api/vulkan.api code-generator.tmpl`
-Similar for `nulldrv/null_driver_gen.*`.
+Install Python3 (if not already installed) and execute below:
+`$ ./scripts/code_generator.py`
diff --git a/vulkan/api/platform.api b/vulkan/api/platform.api
deleted file mode 100644
index a7c4c30..0000000
--- a/vulkan/api/platform.api
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2015 The Khronos Group Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and/or associated documentation files (the
-// "Materials"), to deal in the Materials without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Materials, and to
-// permit persons to whom the Materials are furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Materials.
-//
-// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-
-// Platform types, as defined or included in vk_platform.h
-
-type u64 size_t
-
-// VK_USE_PLATFORM_XLIB_KHR
-@internal class Display {}
-@internal class Window {}
-@internal type u64 VisualID
-
-// VK_USE_PLATFORM_XCB_KHR
-@internal class xcb_connection_t {}
-@internal type u32 xcb_window_t
-@internal type u32 xcb_visualid_t
-
-// VK_USE_PLATFORM_WAYLAND_KHR
-@internal class wl_display {}
-@internal class wl_surface {}
-
-// VK_USE_PLATFORM_MIR_KHR
-@internal class MirConnection {}
-@internal class MirSurface {}
-
-// VK_USE_PLATFORM_ANDROID_KHR
-@internal class ANativeWindow {}
-@internal class AHardwareBuffer {}
-@internal type void* buffer_handle_t
-
-// VK_USE_PLATFORM_WIN32_KHR
-@internal type void* HINSTANCE
-@internal type void* HWND
-@internal type void* HANDLE
-@internal type u32   DWORD
-@internal type u16*  LPCWSTR
-@internal class SECURITY_ATTRIBUTES {}
-
-// VK_USE_PLATFORM_XLIB_XRANDR_EXT
-@internal type u64 RROutput
-
-// VK_USE_PLATFORM_FUCHSIA
-@internal type u32 zx_handle_t
\ No newline at end of file
diff --git a/vulkan/api/templates/asciidoc.tmpl b/vulkan/api/templates/asciidoc.tmpl
deleted file mode 100644
index 3009e19..0000000
--- a/vulkan/api/templates/asciidoc.tmpl
+++ /dev/null
@@ -1,151 +0,0 @@
-{{Include "vulkan_common.tmpl"}}
-{{if not (Global "AsciiDocPath")}}{{Global "AsciiDocPath" "../../doc/specs/vulkan/"}}{{end}}
-{{$ | Macro "AsciiDoc.Main"}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  AsciiDoc generation main entry point.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Main"}}
-  {{$docPath := Global "AsciiDocPath"}}
-
-  {{/* Generate AsciiDoc files for API enums and bitfields (flags). */}}
-  {{range $e := $.Enums}}
-    {{if not $e.IsBitfield}}
-      {{$filename := print $docPath "enums/" (Macro "EnumName" $e) ".txt"}}
-      {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Enum" $e) "File" $filename}}
-    {{else}}
-      {{$filename := print $docPath "flags/" (Macro "EnumName" $e) ".txt"}}
-      {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Flag" $e) "File" $filename}}
-    {{end}}
-  {{end}}
-
-  {{/* Generate AsciiDoc files for API commands (protos). */}}
-  {{range $f := (AllCommands $)}}
-    {{if not (GetAnnotation $f "pfn")}}
-      {{$filename := print $docPath "protos/" $f.Name ".txt"}}
-      {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Proto" $f) "File" $filename}}
-    {{end}}
-  {{end}}
-
-  {{/* Generate AsciiDoc files for API structs. */}}
-  {{range $c := $.Classes}}
-    {{if not (GetAnnotation $c "internal")}}
-      {{$filename := print $docPath "structs/" $c.Name ".txt"}}
-      {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Struct" $c) "File" $filename}}
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the AsciiDoc contents for the specified API enum.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Enum"}}
-  {{AssertType $ "Enum"}}
-
-  {{Macro "Docs" $.Docs}}
-  typedef enum {
-    {{range $i, $e := $.Entries}}
-      {{Macro "EnumEntry" $e}} = {{AsSigned $e.Value}}, {{Macro "Docs" $e.Docs}}
-    {{end}}
-  ¶
-    {{$name := Macro "EnumName" $ | TrimRight "ABCDEFGHIJKLMNOQRSTUVWXYZ" | SplitPascalCase | Upper | JoinWith "_"}}
-    {{$first := Macro "EnumFirstEntry" $}}
-    {{$last  := Macro "EnumLastEntry" $}}
-    {{$name}}_BEGIN_RANGE = {{$first}},
-    {{$name}}_END_RANGE = {{$last}},
-    {{$name}}_NUM = ({{$last}} - {{$first}} + 1),
-    {{$name}}_MAX_ENUM = 0x7FFFFFFF
-  } {{Macro "EnumName" $}};
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the AsciiDoc contents for the specified API bitfield.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Flag"}}
-  {{AssertType $ "Enum"}}
-
-  {{Macro "Docs" $.Docs}}
-  typedef VkFlags {{Macro "EnumName" $}};
-  {{if $.Entries}}
-  typedef enum {
-  {{range $e := $.Entries}}
-    {{Macro "BitfieldEntryName" $e}} = {{printf "%#.8x" $e.Value}}, {{Macro "Docs" $e.Docs}}
-  {{end}}
-  } {{Macro "EnumName" $ | TrimRight "s"}}Bits;
-  {{end}}
-{{end}}
-
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the AsciiDoc contents for the specified API class.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Struct"}}
-  {{AssertType $ "Class"}}
-
-  {{Macro "Docs" $.Docs}}
-  typedef {{if GetAnnotation $ "union"}}union{{else}}struct{{end}} {
-    {{range $f := $.Fields}}
-      {{Node "Type" $f}} {{$f.Name}}{{Macro "ArrayPostfix" (TypeOf $f)}}; {{Macro "Docs" $f.Docs}}
-    {{end}}
-  } {{Macro "StructName" $}};
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the AsciiDoc contents for the specified API function.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Proto"}}
-  {{AssertType $ "Function"}}
-
-  {{Macro "Docs" $.Docs}}
-  {{Node "Type" $.Return}} VKAPI {{Macro "FunctionName" $}}({{Macro "Parameters" $}});
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Wraps the specified Code in AsciiDoc source tags then writes to the specified File.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Write"}}
-  {{AssertType $.Code "string"}}
-  {{AssertType $.File "string"}}
-
-  {{$code := $.Code | Format (Global "clang-format")}}
-  {{JoinWith "\n" (Macro "AsciiDoc.Header") $code (Macro "AsciiDoc.Footer") ""| Write $.File}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits an AsciiDoc source header.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Header"}}
-[source,{basebackend@docbook:c++:cpp}]
-------------------------------------------------------------------------------
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits an AsciiDoc source footer.
--------------------------------------------------------------------------------
-*/}}
-{{define "AsciiDoc.Footer"}}
-------------------------------------------------------------------------------
-{{end}}
diff --git a/vulkan/api/templates/vk_xml.tmpl b/vulkan/api/templates/vk_xml.tmpl
deleted file mode 100644
index 893bde7..0000000
--- a/vulkan/api/templates/vk_xml.tmpl
+++ /dev/null
@@ -1,435 +0,0 @@
-{{Include "vulkan_common.tmpl"}}
-{{Macro "DefineGlobals" $}}
-{{$ | Macro "vk.xml" | Reflow 4 | Write "vk.xml"}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Entry point
--------------------------------------------------------------------------------
-*/}}
-{{define "vk.xml"}}
-<?xml version="1.0" encoding="UTF-8"?>
-<registry>
-    »<comment>«
-Copyright (c) 2015 The Khronos Group Inc.

-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and/or associated documentation files (the
-"Materials"), to deal in the Materials without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Materials, and to
-permit persons to whom the Materials are furnished to do so, subject to
-the following conditions:

-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Materials.

-THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.

-------------------------------------------------------------------------

-This file, vk.xml, is the Vulkan API Registry.»
-    </comment>

-    <!-- SECTION: Vulkan type definitions -->
-    <types>»
-        <type name="vk_platform" category="include">#include "vk_platform.h"</type>

-        <type category="define">#define <name>VK_MAKE_VERSION</name>(major, minor, patch) \
-    «((major &lt;&lt; 22) | (minor &lt;&lt; 12) | patch)</type>»

-        <type category="define">// Vulkan API version supported by this file««
-#define <name>VK_API_VERSION</name> <type>VK_MAKE_VERSION</type>({{Global "VERSION_MAJOR"}}, {{Global "VERSION_MINOR"}}, {{Global "VERSION_PATCH"}})</type>

-        »»<type category="define">««
-#if (_MSC_VER &gt;= 1800 || __cplusplus &gt;= 201103L)
-#define <name>VK_NONDISP_HANDLE_OPERATOR_BOOL</name>() explicit operator bool() const { return handle != 0; }
-#else
-#define VK_NONDISP_HANDLE_OPERATOR_BOOL()
-«#endif
-      »»»</type>

-      <type category="define">«««
-#define <name>VK_DEFINE_HANDLE</name>(obj) typedef struct obj##_T* obj;</type>
-      »»»<type category="define">«««
-#if defined(__cplusplus)
-    »»#if (_MSC_VER &gt;= 1800 || __cplusplus &gt;= 201103L)
-        »// The bool operator only works if there are no implicit conversions from an obj to
-        // a bool-compatible type, which can then be used to unintentionally violate type safety.
-        // C++11 and above supports the "explicit" keyword on conversion operators to stop this
-        // from happening. Otherwise users of C++ below C++11 won't get direct access to evaluating
-        // the object handle as a bool in expressions like:
-        //     if (obj) vkDestroy(obj);
-        #define VK_NONDISP_HANDLE_OPERATOR_BOOL() explicit operator bool() const { return handle != 0; }
-        #define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \
-            explicit obj(uint64_t x) : handle(x) { } \
-            obj(decltype(nullptr)) : handle(0) { }
-    «#else»
-        #define VK_NONDISP_HANDLE_OPERATOR_BOOL()
-        #define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \
-            obj(uint64_t x) : handle(x) { }
-    «#endif
-    #define <name>VK_DEFINE_NONDISP_HANDLE</name>(obj) \»
-        struct obj { \
-            obj() { } \
-            VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \
-            obj&amp; operator =(uint64_t x) { handle = x; return *this; } \
-            bool operator==(const obj&amp; other) const { return handle == other.handle; } \
-            bool operator!=(const obj&amp; other) const { return handle != other.handle; } \
-            bool operator!() const { return !handle; } \
-            VK_NONDISP_HANDLE_OPERATOR_BOOL() \
-            uint64_t handle; \
-        };
-««#else
-    »#define VK_DEFINE_NONDISP_HANDLE(obj) typedef struct obj##_T { uint64_t handle; } obj;«
-#endif
-        »»</type>

-        <type category="define">
-#if defined(__cplusplus) &amp;&amp; ((defined(_MSC_VER) &amp;&amp; _MSC_VER &gt;= 1800) || __cplusplus &gt;= 201103L)
-    »#define <name>VK_NULL_HANDLE</name> nullptr
-«#else
-    »#define VK_NULL_HANDLE 0
-«#endif
-        »»</type>

-        <type requires="vk_platform" name="VkDeviceSize"/>
-        <type requires="vk_platform" name="VkSampleMask"/>
-        <type requires="vk_platform" name="VkFlags"/>
-        <!-- Basic C types, pulled in via vk_platform.h -->
-        <type requires="vk_platform" name="char"/>
-        <type requires="vk_platform" name="float"/>
-        <type requires="vk_platform" name="VkBool32"/>
-        <type requires="vk_platform" name="uint8_t"/>
-        <type requires="vk_platform" name="uint32_t"/>
-        <type requires="vk_platform" name="uint64_t"/>
-        <type requires="vk_platform" name="int32_t"/>
-        <type requires="vk_platform" name="size_t"/>
-        <!-- Bitfield types -->
-        {{range $e := $.Enums}}
-          {{if $e.IsBitfield}}
-            {{$bits := print (Macro "EnumName" $e | TrimRight "s") "Bits"}}
-            <type{{if $e.Entries}} requires="{{$bits}}"{{end}} category="bitmask">typedef <type>VkFlags</type> <name>{{$e.Name}}</name>;</type>§
-            {{if $e.Entries}}{{Macro "XML.Docs" $e.Docs}}
-            {{else}}{{Macro "XML.Docs" (Strings $e.Docs "(no bits yet)")}}
-            {{end}}
-          {{end}}
-        {{end}}

-        <!-- Types which can be void pointers or class pointers, selected at compile time -->
-        {{range $i, $p := $.Pseudonyms}}
-          {{     if (GetAnnotation $p "dispatchHandle")}}
-            {{if Global "VK_DEFINE_HANDLE_TYPE_DEFINED"}}
-              <type category="handle">VK_DEFINE_HANDLE(<name>{{$p.Name}}</name>)</type>
-            {{else}}
-              {{Global "VK_DEFINE_HANDLE_TYPE_DEFINED" "YES"}}
-              <type category="handle"><type>VK_DEFINE_HANDLE</type>(<name>{{$p.Name}}</name>)</type>
-            {{end}}
-          {{else if (GetAnnotation $p "nonDispatchHandle")}}
-            {{if Global "VK_DEFINE_NONDISP_HANDLE_TYPE_DEFINED"}}
-              <type category="handle">VK_DEFINE_NONDISP_HANDLE(<name>{{$p.Name}}</name>)</type>
-            {{else}}
-              {{Global "VK_DEFINE_NONDISP_HANDLE_TYPE_DEFINED" "YES"}}
-              <type category="handle"><type>VK_DEFINE_NONDISP_HANDLE</type>(<name>{{$p.Name}}</name>)</type>
-            {{end}}
-          {{end}}
-        {{end}}

-        <!-- Types generated from corresponding <enums> tags below -->
-        {{range $e := SortBy $.Enums "EnumName"}}
-          {{if and $e.Entries (not (GetAnnotation $e "internal"))}}
-            {{if $e.IsBitfield}}
-              <type name="{{Macro "EnumName" $e | TrimRight "s"}}Bits" category="enum"/>
-            {{else}}
-              <type name="{{$e.Name}}" category="enum"/>
-            {{end}}
-          {{end}}
-        {{end}}

-        <!-- The PFN_vk*Function types are used by VkAllocCallbacks below -->
-        <type>typedef void* (VKAPI *<name>PFN_vkAllocFunction</name>)(«
-          void*                           pUserData,
-          size_t                          size,
-          size_t                          alignment,
-          <type>VkSystemAllocType</type>               allocType);</type>»
-        <type>typedef void (VKAPI *<name>PFN_vkFreeFunction</name>)(«
-          void*                           pUserData,
-          void*                           pMem);</type>»

-        <!-- The PFN_vkVoidFunction type are used by VkGet*ProcAddr below -->
-        <type>typedef void (VKAPI *<name>PFN_vkVoidFunction</name>)(void);</type>

-        <!-- Struct types -->
-        {{range $c := $.Classes}}
-          {{if not (GetAnnotation $c "internal")}}
-            {{Macro "Struct" $c}}
-          {{end}}
-        {{end}}
-    «</types>

-    <!-- SECTION: Vulkan enumerant (token) definitions. -->

-    <enums namespace="VK" comment="Misc. hardcoded constants - not an enumerated type">»
-            <!-- This is part of the header boilerplate -->
-      {{range $d := $.Definitions}}
-        {{if HasPrefix $d.Name "VK_"}}
-        <enum value="{{$d.Expression}}"        name="{{$d.Name}}"/>{{Macro "XML.Docs" $d.Docs}}
-        {{end}}
-      {{end}}
-        <enum value="1000.0f"  name="VK_LOD_CLAMP_NONE"/>
-        <enum value="(-0U)" name="VK_REMAINING_MIP_LEVELS"/>
-        <enum value="(~0U)" name="VK_REMAINING_ARRAY_LAYERS"/>
-        <enum value="(_0ULL)" name="VK_WHOLE_SIZE"/>
-        <enum value="(~0U)" name="VK_ATTACHMENT_UNUSED"/>
-        <enum value="(~0U)" name="VK_QUEUE_FAMILY_IGNORED"/>
-        <enum value="(~0U)" name="VK_SUBPASS_EXTERNAL"/>
-    «</enums>

-    <!-- Unlike OpenGL, most tokens in Vulkan are actual typed enumerants in»
-         their own numeric namespaces. The "name" attribute is the C enum
-         type name, and is pulled in from a <type> definition above
-         (slightly clunky, but retains the type / enum distinction). "type"
-         attributes of "enum" or "bitmask" indicate that these values should
-         be generated inside an appropriate definition. -->«

-    {{range $e := $.Enums}}
-      {{if not (or $e.IsBitfield (GetAnnotation $e "internal"))}}
-        {{Macro "XML.Enum" $e}}
-      {{end}}
-    {{end}}

-    <!-- Flags -->
-    {{range $e := $.Enums}}
-      {{if $e.IsBitfield}}
-        {{Macro "XML.Bitfield" $e}}
-      {{end}}
-    {{end}}

-    <!-- SECTION: Vulkan command definitions -->
-    <commands namespace="vk">»
-    {{range $f := AllCommands $}}
-      {{if not (GetAnnotation $f "pfn")}}
-        {{Macro "XML.Function" $f}}
-      {{end}}
-    {{end}}
-    «</commands>

-    <!-- SECTION: Vulkan API interface definitions -->
-    <feature api="vulkan" name="VK_VERSION_1_0" number="1.0">»
-        <require comment="Header boilerplate">»
-            <type name="vk_platform"/>
-        «</require>
-        <require comment="API version">»
-            <type name="VK_API_VERSION"/>
-        «</require>
-        <require comment="API constants">»
-            <enum name="VK_LOD_CLAMP_NONE"/>
-            <enum name="VK_REMAINING_MIP_LEVELS"/>
-            <enum name="VK_REMAINING_ARRAY_LAYERS"/>
-            <enum name="VK_WHOLE_SIZE"/>
-            <enum name="VK_ATTACHMENT_UNUSED"/>
-            <enum name="VK_TRUE"/>
-            <enum name="VK_FALSE"/>
-        «</require>
-        <require comment="All functions (TODO: split by type)">»
-        {{range $f := AllCommands $}}
-          {{if not (GetAnnotation $f "pfn")}}
-          <command name="{{$f.Name}}"/>
-          {{end}}
-        {{end}}
-        </require>
-        «<require comment="Types not directly used by the API">»
-            <!-- Include <type name="typename"/> here for e.g. structs that»
-                 are not parameter types of commands, but still need to be
-                 defined in the API.
-             «-->
-            <type name="VkBufferMemoryBarrier"/>
-            <type name="VkDispatchIndirectCmd"/>
-            <type name="VkDrawIndexedIndirectCmd"/>
-            <type name="VkDrawIndirectCmd"/>
-            <type name="VkImageMemoryBarrier"/>
-            <type name="VkMemoryBarrier"/>
-        «</require>
-    «</feature>

-    <!-- SECTION: Vulkan extension interface definitions (none yet) -->
-«</registry>
-{{end}}
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C declaration for the specified bitfield.
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.Bitfield"}}
-  {{AssertType $ "Enum"}}
-
-  {{if $.Entries}}
-  <enums namespace="VK" name="{{Macro "EnumName" $ | TrimRight "s"}}Bits" type="bitmask">»
-  {{range $e := $.Entries}}
-    {{$pos := Bitpos $e.Value}}
-    <enum §
-      {{if gt $pos -1}} bitpos="{{$pos}}"    §
-      {{else}}value="{{if $e.Value}}{{printf "0x%.8X" $e.Value}}{{else}}0{{end}}"    §
-      {{end}}name="{{Macro "BitfieldEntryName" $e}}" §
-      {{if $d := $e.Docs}} comment="{{$d | JoinWith "  "}}"{{end}}/>
-  {{end}}
-  «</enums>
-  {{end}}
-
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C declaration for the specified enum.
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.Enum"}}
-  {{AssertType $ "Enum"}}
-
-  <enums namespace="VK" name="{{Macro "EnumName" $}}" type="enum" §
-         expand="{{Macro "EnumName" $ | SplitPascalCase | Upper | JoinWith "_"}}"{{if $.Docs}} comment="{{$.Docs | JoinWith "  "}}"{{end}}>»
-  {{range $i, $e := $.Entries}}
-    <enum value="{{AsSigned $e.Value}}"     name="{{Macro "BitfieldEntryName" $e}}"{{if $e.Docs}} comment="{{$e.Docs | JoinWith "  "}}"{{end}}/>
-  {{end}}
-  {{if $lu := GetAnnotation $ "lastUnused"}}
-    <unused start="{{index $lu.Arguments 0}}"/>
-  {{end}}
-  «</enums>
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C declaration for the specified class.
--------------------------------------------------------------------------------
-*/}}
-{{define "Struct"}}
-  {{AssertType $ "Class"}}
-
-  <type category="{{Macro "StructType" $}}" name="{{Macro "StructName" $}}"{{if $.Docs}} comment="{{$.Docs | JoinWith "  "}}"{{end}}>»
-    {{range $f := $.Fields}}
-    <member>{{Node "XML.Type" $f}}        <name>{{$f.Name}}</name>{{Macro "XML.ArrayPostfix" $f}}</member>{{Macro "XML.Docs" $f.Docs}}
-    {{end}}
-  «</type>
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits either 'struct' or 'union' for the specified class.
--------------------------------------------------------------------------------
-*/}}
-{{define "StructType"}}
-  {{AssertType $ "Class"}}
-
-  {{if GetAnnotation $ "union"}}union{{else}}struct{{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C function pointer typedef declaration for the specified command.
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.Function"}}
-  {{AssertType $ "Function"}}
-
-    {{$ts := GetAnnotation $ "threadSafety"}}
-    <command{{if $ts}} threadsafe="{{index $ts.Arguments 0}}"{{end}}>»
-        <proto>{{Node "XML.Type" $.Return}} <name>{{$.Name}}</name></proto>
-        {{range $p := $.CallParameters}}
-          <param>{{Node "XML.Type" $p}} <name>{{$p.Name}}{{Macro "ArrayPostfix" $p}}</name></param>
-        {{end}}
-    «</command>
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the XML translation for the specified documentation block (string array).
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.Docs"}}
-  {{if $}} <!-- {{JoinWith "  " $ | Replace "<" "" | Replace ">" ""}} -->{{end}}
-{{end}}
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C translation for the specified type.
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.Type.Class"      }}<type>{{Macro "StructName" $.Type}}</type>{{end}}
-{{define "XML.Type.Pseudonym"  }}<type>{{$.Type.Name}}</type>{{end}}
-{{define "XML.Type.Enum"       }}<type>{{$.Type.Name}}</type>{{end}}
-{{define "XML.Type.StaticArray"}}{{Node "XML.Type" $.Type.ValueType}}{{end}}
-{{define "XML.Type.Pointer"    }}{{if $.Type.Const}}{{Node "XML.ConstType" $.Type.To}}{{else}}{{Node "XML.Type" $.Type.To}}{{end}}*{{end}}
-{{define "XML.Type.Slice"      }}<type>{{Node "XML.Type" $.Type.To}}</type>*{{end}}
-{{define "XML.Type#s8"         }}<type>int8_t</type>{{end}}
-{{define "XML.Type#u8"         }}<type>uint8_t</type>{{end}}
-{{define "XML.Type#s16"        }}<type>int16_t</type>{{end}}
-{{define "XML.Type#u16"        }}<type>uint16_t</type>{{end}}
-{{define "XML.Type#s32"        }}<type>int32_t</type>{{end}}
-{{define "XML.Type#u32"        }}<type>uint32_t</type>{{end}}
-{{define "XML.Type#f32"        }}<type>float</type>{{end}}
-{{define "XML.Type#s64"        }}<type>int64_t</type>{{end}}
-{{define "XML.Type#u64"        }}<type>uint64_t</type>{{end}}
-{{define "XML.Type#f64"        }}<type>double</type>{{end}}
-{{define "XML.Type#char"       }}<type>char</type>{{end}}
-{{define "XML.Type#void"       }}void{{end}}
-
-{{define "XML.ConstType_Default"}}const {{Node "XML.Type" $.Type}}{{end}}
-{{define "XML.ConstType.Pointer"}}{{Node "XML.Type" $.Type}} const{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a C type and name for the given parameter
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.Parameter"}}
-  {{AssertType $ "Parameter"}}
-
-  <type>{{Macro "ParameterType" $}}</type> <name>{{$.Name}}{{Macro "ArrayPostfix" $}}</name>
-{{end}}
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a comma-separated list of C type-name paired parameters for the given
-  command.
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.Parameters"}}
-  {{AssertType $ "Function"}}
-
-  {{ForEach $.CallParameters "XML.Parameter" | JoinWith ", "}}
-  {{if not $.CallParameters}}<type>void</type>{{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the fixed-size-array postfix for pseudonym types annotated with @array
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.ArrayPostfix"}}{{Node "XML.ArrayPostfix" $}}{{end}}
-{{define "XML.ArrayPostfix.StaticArray"}}[{{Node "XML.NamedValue" $.Type.SizeExpr}}]{{end}}
-{{define "XML.ArrayPostfix_Default"}}{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the value of the given constant, or the <enum> tagged name if existant.
--------------------------------------------------------------------------------
-*/}}
-{{define "XML.NamedValue.Definition"}}<enum>{{$.Node.Name}}</enum>{{end}}
-{{define "XML.NamedValue.EnumEntry"}}<enum>{{$.Node.Name}}</enum>{{end}}
-{{define "XML.NamedValue_Default"}}{{$.Node}}{{end}}
diff --git a/vulkan/api/templates/vulkan_common.tmpl b/vulkan/api/templates/vulkan_common.tmpl
deleted file mode 100644
index f694c56..0000000
--- a/vulkan/api/templates/vulkan_common.tmpl
+++ /dev/null
@@ -1,223 +0,0 @@
-{{$clang_style := "{BasedOnStyle: Google, AccessModifierOffset: -4, ColumnLimit: 200, ContinuationIndentWidth: 8, IndentWidth: 4, AlignOperands: true, CommentPragmas: '.*'}"}}
-{{Global "clang-format" (Strings "clang-format" "-style" $clang_style)}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C translation for the specified type.
--------------------------------------------------------------------------------
-*/}}
-{{define "Type.Class"      }}{{if GetAnnotation $.Type "internal"}}struct {{end}}{{Macro "StructName" $.Type}}{{end}}
-{{define "Type.Pseudonym"  }}{{$.Type.Name}}{{end}}
-{{define "Type.Enum"       }}{{$.Type.Name}}{{end}}
-{{define "Type.StaticArray"}}{{Node "Type" $.Type.ValueType}}{{end}}
-{{define "Type.Pointer"    }}{{if $.Type.Const}}{{Node "ConstType" $.Type.To}}{{else}}{{Node "Type" $.Type.To}}{{end}}*{{end}}
-{{define "Type.Slice"      }}{{Log "%T %+v" $.Node $.Node}}{{Node "Type" $.Type.To}}*{{end}}
-{{define "Type#bool"       }}bool{{end}}
-{{define "Type#int"        }}int{{end}}
-{{define "Type#uint"       }}unsigned int{{end}}
-{{define "Type#s8"         }}int8_t{{end}}
-{{define "Type#u8"         }}uint8_t{{end}}
-{{define "Type#s16"        }}int16_t{{end}}
-{{define "Type#u16"        }}uint16_t{{end}}
-{{define "Type#s32"        }}int32_t{{end}}
-{{define "Type#u32"        }}uint32_t{{end}}
-{{define "Type#f32"        }}float{{end}}
-{{define "Type#s64"        }}int64_t{{end}}
-{{define "Type#u64"        }}uint64_t{{end}}
-{{define "Type#f64"        }}double{{end}}
-{{define "Type#void"       }}void{{end}}
-{{define "Type#char"       }}char{{end}}
-
-{{define "ConstType_Default"}}const {{Node "Type" $.Type}}{{end}}
-{{define "ConstType.Pointer"}}{{Node "Type" $.Type}} const{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C translation for the specified documentation block (string array).
--------------------------------------------------------------------------------
-*/}}
-{{define "Docs"}}
-  {{if $}}// {{$ | JoinWith "\n// "}}{{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the name of a bitfield entry.
--------------------------------------------------------------------------------
-*/}}
-{{define "BitfieldEntryName"}}
-  {{AssertType $ "EnumEntry"}}
-
-  {{Macro "EnumEntry" $}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the name of an enum type.
--------------------------------------------------------------------------------
-*/}}
-{{define "EnumName"}}{{AssertType $ "Enum"}}{{$.Name}}{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the name of an enum entry.
--------------------------------------------------------------------------------
-*/}}
-{{define "EnumEntry"}}
-  {{AssertType $.Owner "Enum"}}
-  {{AssertType $.Name "string"}}
-
-  {{$.Name}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the name of the first entry of an enum.
--------------------------------------------------------------------------------
-*/}}
-{{define "EnumFirstEntry"}}
-  {{AssertType $ "Enum"}}
-
-  {{range $i, $e := $.Entries}}
-    {{if not $i}}{{$e.Name}}{{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the name of the last entry of an enum.
--------------------------------------------------------------------------------
-*/}}{{define "EnumLastEntry"}}
-  {{AssertType $ "Enum"}}
-
-  {{range $i, $e := $.Entries}}
-    {{if not (HasMore $i $.Entries)}}{{$e.Name}}{{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the name of a struct (class) type.
--------------------------------------------------------------------------------
-*/}}
-{{define "StructName"}}{{AssertType $ "Class"}}{{$.Name}}{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the name of a function.
--------------------------------------------------------------------------------
-*/}}
-{{define "FunctionName"}}{{AssertType $ "Function"}}{{$.Name}}{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the fixed-size-array postfix for pseudonym types annotated with @array
--------------------------------------------------------------------------------
-*/}}
-{{define "ArrayPostfix"}}{{Node "ArrayPostfix" $}}{{end}}
-{{define "ArrayPostfix.StaticArray"}}[{{$.Type.Size}}]{{end}}
-{{define "ArrayPostfix_Default"}}{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a C type and name for the given parameter
--------------------------------------------------------------------------------
-*/}}
-{{define "Parameter"}}
-  {{AssertType $ "Parameter"}}
-
-  {{if GetAnnotation $ "readonly"}}const {{end}}{{Macro "ParameterType" $}} {{$.Name}}{{Macro "ArrayPostfix" $}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a C name for the given parameter
--------------------------------------------------------------------------------
-*/}}
-{{define "ParameterName"}}
-  {{AssertType $ "Parameter"}}
-
-  {{$.Name}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a C type for the given parameter
--------------------------------------------------------------------------------
-*/}}
-{{define "ParameterType"}}{{AssertType $ "Parameter"}}{{Node "Type" $}}{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a comma-separated list of C type-name paired parameters for the given
-  command.
--------------------------------------------------------------------------------
-*/}}
-{{define "Parameters"}}
-  {{AssertType $ "Function"}}
-
-  {{ForEach $.CallParameters "Parameter" | JoinWith ", "}}
-  {{if not $.CallParameters}}void{{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C function pointer name for the specified command.
--------------------------------------------------------------------------------
-*/}}
-{{define "FunctionPtrName"}}
-  {{AssertType $ "Function"}}
-
-  PFN_{{$.Name}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Parses const variables as text Globals.
--------------------------------------------------------------------------------
-*/}}
-{{define "DefineGlobals"}}
-  {{AssertType $ "API"}}
-
-  {{range $d := $.Definitions}}
-    {{Global $d.Name $d.Expression}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Given a function, return "Global", "Instance", or "Device" depending on which
-  dispatch table the function belongs to.
--------------------------------------------------------------------------------
-*/}}
-{{define "Vtbl#VkInstance"      }}Instance{{end}}
-{{define "Vtbl#VkPhysicalDevice"}}Instance{{end}}
-{{define "Vtbl#VkDevice"        }}Device{{end}}
-{{define "Vtbl#VkQueue"         }}Device{{end}}
-{{define "Vtbl#VkCommandBuffer" }}Device{{end}}
-{{define "Vtbl_Default"         }}Global{{end}}
-{{define "Vtbl"}}
-  {{AssertType $ "Function"}}
-
-  {{if gt (len $.CallParameters) 0}}
-    {{Node "Vtbl" (index $.CallParameters 0)}}
-  {{else}}Global
-  {{end}}
-{{end}}
diff --git a/vulkan/api/templates/vulkan_h.tmpl b/vulkan/api/templates/vulkan_h.tmpl
deleted file mode 100644
index 83a5e40..0000000
--- a/vulkan/api/templates/vulkan_h.tmpl
+++ /dev/null
@@ -1,295 +0,0 @@
-{{Include "vulkan_common.tmpl"}}
-{{Macro "DefineGlobals" $}}
-{{$ | Macro "vulkan.h" | Format (Global "clang-format") | Write "../include/vulkan.h"}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Entry point
--------------------------------------------------------------------------------
-*/}}
-{{define "vulkan.h"}}
-#ifndef __vulkan_h_
-#define __vulkan_h_ 1

-#ifdef __cplusplus
-extern "C" {
-#endif

-/*
-** Copyright (c) 2015-2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a
-** copy of this software and/or associated documentation files (the
-** "Materials"), to deal in the Materials without restriction, including
-** without limitation the rights to use, copy, modify, merge, publish,
-** distribute, sublicense, and/or sell copies of the Materials, and to
-** permit persons to whom the Materials are furnished to do so, subject to
-** the following conditions:
-**
-** The above copyright notice and this permission notice shall be included
-** in all copies or substantial portions of the Materials.
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-*/

-/*
-** This header is generated from the Khronos Vulkan API Registry.
-**
-*/

-#define VK_VERSION_1_0 1
-#include "vk_platform.h"

-#define VK_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))

-// Vulkan API version supported by this file
-#define VK_API_VERSION \
-    VK_MAKE_VERSION({{Global "VERSION_MAJOR"}}, {{Global "VERSION_MINOR"}}, {{Global "VERSION_PATCH"}})

-#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22)
-#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff)
-#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff)

-#if defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1800 || __cplusplus >= 201103L)
-    #define VK_NULL_HANDLE nullptr
-#else
-    #define VK_NULL_HANDLE 0
-#endif

-#define VK_DEFINE_HANDLE(obj) typedef struct obj##_T* obj;

-#if defined(__cplusplus)
-#if ((defined(_MSC_VER) && _MSC_VER >= 1800 || __cplusplus >= 201103L)
-// The bool operator only works if there are no implicit conversions from an obj to
-// a bool-compatible type, which can then be used to unintentionally violate type safety.
-// C++11 and above supports the "explicit" keyword on conversion operators to stop this
-// from happening. Otherwise users of C++ below C++11 won't get direct access to evaluating
-// the object handle as a bool in expressions like:
-//     if (obj) vkDestroy(obj);
-#define VK_NONDISP_HANDLE_OPERATOR_BOOL() \
-    explicit operator bool() const { return handle != 0; }
-#define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \
-    explicit obj(uint64_t x) : handle(x) { } \
-    obj(decltype(nullptr)) : handle(0) { }
-#else
-#define VK_NONDISP_HANDLE_OPERATOR_BOOL()
-#define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \
-    obj(uint64_t x) : handle(x) { }
-#endif
-#define VK_DEFINE_NONDISP_HANDLE(obj)                                              \
-    struct obj {                                                                   \
-        obj() : handle(0) { }                                                      \
-        VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj)                             \
-        obj& operator=(uint64_t x) {                                               \
-            handle = x;                                                            \
-            return *this;                                                          \
-        }                                                                          \
-        bool operator==(const obj& other) const { return handle == other.handle; } \
-        bool operator!=(const obj& other) const { return handle != other.handle; } \
-        bool operator!() const { return !handle; }                                 \
-        VK_NONDISP_HANDLE_OPERATOR_BOOL()                                          \
-        uint64_t handle;                                                           \
-    };
-#else
-#define VK_DEFINE_NONDISP_HANDLE(obj) \
-    typedef struct obj##_T { uint64_t handle; } obj;
-#endif

-#define VK_LOD_CLAMP_NONE         1000.0f
-#define VK_REMAINING_MIP_LEVELS   (~0U)
-#define VK_REMAINING_ARRAY_LAYERS (~0U)
-#define VK_WHOLE_SIZE             (~0ULL)
-#define VK_ATTACHMENT_UNUSED      (~0U)
-define VK_QUEUE_FAMILY_IGNORED    (~0U)
-define VK_SUBPASS_EXTERNAL        (~0U)
-{{range $d := $.Definitions}}
-  {{if HasPrefix $d.Name "VK_"}}#define {{$d.Name}}  {{$d.Expression}}{{end}}
-{{end}}

-{{range $i, $p := $.Pseudonyms}}
-  {{if GetAnnotation $p "dispatchHandle"}}VK_DEFINE_HANDLE({{$p.Name}})
-  {{else if GetAnnotation $p "nonDispatchHandle"}}VK_DEFINE_NONDISP_HANDLE({{$p.Name}})
-  {{end}}
-{{end}}

-// ------------------------------------------------------------------------------------------------
-// Enumerations

-  {{range $e := $.Enums}}
-    {{if not $e.IsBitfield}}
-      {{Macro "Enum" $e}}
-    {{end}}
-  {{end}}

-// ------------------------------------------------------------------------------------------------
-// Flags

-  {{range $e := $.Enums}}
-    {{if $e.IsBitfield}}
-      {{Macro "Bitfield" $e}}
-    {{end}}
-  {{end}}

-// ------------------------------------------------------------------------------------------------
-// Vulkan structures

-  {{/* Function pointers */}}
-  {{range $f := AllCommands $}}
-    {{if GetAnnotation $f "pfn"}}
-      {{Macro "FunctionTypedef" $f}}
-    {{end}}
-  {{end}}

-  {{range $c := $.Classes}}
-    {{if not (GetAnnotation $c "internal")}}
-      {{Macro "Struct" $c}}
-    {{end}}
-  {{end}}

-// ------------------------------------------------------------------------------------------------
-// API functions

-  {{range $f := AllCommands $}}
-    {{if not (GetAnnotation $f "pfn")}}
-      {{Macro "FunctionTypedef" $f}}
-    {{end}}
-  {{end}}

-#ifdef VK_NO_PROTOTYPES

-  {{range $f := AllCommands $}}
-    {{if not (GetAnnotation $f "pfn")}}
-      {{Macro "FunctionDecl" $f}}
-    {{end}}
-  {{end}}

-#endif

-#ifdef __cplusplus
-}
-#endif

-#endif
-{{end}}
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C declaration for the specified bitfield.
--------------------------------------------------------------------------------
-*/}}
-{{define "Bitfield"}}
-  {{AssertType $ "Enum"}}
-
-  {{Macro "Docs" $.Docs}}
-  typedef VkFlags {{Macro "EnumName" $}};
-  {{if $.Entries}}
-  typedef enum {
-  {{range $b := $.Entries}}
-    {{Macro "BitfieldEntryName" $b}} = {{printf "0x%.8X" $b.Value}}, {{Macro "Docs" $b.Docs}}
-  {{end}}
-  } {{Macro "EnumName" $ | TrimRight "s"}}Bits;
-  {{end}}
-  ¶
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C declaration for the specified enum.
--------------------------------------------------------------------------------
-*/}}
-{{define "Enum"}}
-  {{AssertType $ "Enum"}}
-
-  {{Macro "Docs" $.Docs}}
-  typedef enum {
-    {{range $i, $e := $.Entries}}
-      {{Macro "EnumEntry" $e}} = {{printf "0x%.8X" $e.Value}}, {{Macro "Docs" $e.Docs}}
-    {{end}}
-  ¶
-    {{$name  := Macro "EnumName" $ | TrimRight "ABCDEFGHIJKLMNOQRSTUVWXYZ" | SplitPascalCase | Upper | JoinWith "_"}}
-    {{if GetAnnotation $ "enumMaxOnly"}}
-      VK_MAX_ENUM({{$name | SplitOn "VK_"}})
-    {{else}}
-      {{$first := Macro "EnumFirstEntry" $ | SplitOn $name | TrimLeft "_"}}
-      {{$last  := Macro "EnumLastEntry" $  | SplitOn $name | TrimLeft "_"}}
-      VK_ENUM_RANGE({{$name | SplitOn "VK_"}}, {{$first}}, {{$last}})
-    {{end}}
-  } {{Macro "EnumName" $}};
-  ¶
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C declaration for the specified class.
--------------------------------------------------------------------------------
-*/}}
-{{define "Struct"}}
-  {{AssertType $ "Class"}}
-
-  {{Macro "Docs" $.Docs}}
-  typedef {{Macro "StructType" $}} {
-    {{ForEach $.Fields "Field" | JoinWith "\n"}}
-  } {{Macro "StructName" $}};
-  ¶
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C declaration for the specified class field.
--------------------------------------------------------------------------------
-*/}}
-{{define "Field"}}
-  {{AssertType $ "Field"}}
-
-  {{Node "Type" $}} {{$.Name}}§
-  {{Macro "ArrayPostfix" (TypeOf $)}}; {{Macro "Docs" $.Docs}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits either 'struct' or 'union' for the specified class.
--------------------------------------------------------------------------------
-*/}}
-{{define "StructType"}}
-  {{AssertType $ "Class"}}
-
-  {{if GetAnnotation $ "union"}}union{{else}}struct{{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C function pointer typedef declaration for the specified command.
--------------------------------------------------------------------------------
-*/}}
-{{define "FunctionTypedef"}}
-  {{AssertType $ "Function"}}
-
-  typedef {{Node "Type" $.Return}} (VKAPI* {{Macro "FunctionPtrName" $}})({{Macro "Parameters" $}});
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits the C function declaration for the specified command.
--------------------------------------------------------------------------------
-*/}}
-{{define "FunctionDecl"}}
-  {{AssertType $ "Function"}}
-
-  {{if not (GetAnnotation $ "fptr")}}
-    {{Macro "Docs" $.Docs}}
-    {{Node "Type" $.Return}} VKAPI {{Macro "FunctionName" $}}({{Macro "Parameters" $}});
-  {{end}}
-{{end}}
diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api
deleted file mode 100644
index 76503c8..0000000
--- a/vulkan/api/vulkan.api
+++ /dev/null
@@ -1,12163 +0,0 @@
-// Copyright (c) 2015 The Khronos Group Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and/or associated documentation files (the
-// "Materials"), to deal in the Materials without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Materials, and to
-// permit persons to whom the Materials are furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Materials.
-//
-// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-
-import platform "platform.api"
-
-///////////////
-// Constants //
-///////////////
-
-// API version (major.minor.patch)
-define VERSION_MAJOR 1
-define VERSION_MINOR 1
-define VERSION_PATCH 96
-
-// API limits
-define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256
-define VK_UUID_SIZE                     16
-define VK_MAX_EXTENSION_NAME_SIZE       256
-define VK_MAX_DESCRIPTION_SIZE          256
-define VK_MAX_MEMORY_TYPES              32
-define VK_MAX_MEMORY_HEAPS              16    /// The maximum number of unique memory heaps, each of which supporting 1 or more memory types.
-@vulkan1_1
-define VK_MAX_DEVICE_GROUP_SIZE         32
-@vulkan1_1
-define VK_LUID_SIZE                     8
-@vulkan1_1
-define VK_QUEUE_FAMILY_EXTERNAL         -2
-@extension("VK_EXT_queue_family_foreign")
-define VK_QUEUE_FAMILY_FOREIGN_EXT      -3
-@extension("VK_MAX_DRIVER_NAME_SIZE_KHR") // 197
-define VK_MAX_DRIVER_NAME_SIZE_KHR      256
-@extension("VK_MAX_DRIVER_NAME_SIZE_KHR") // 197
-define VK_MAX_DRIVER_INFO_SIZE_KHR      256
-
-// API keywords
-define VK_TRUE        1
-define VK_FALSE       0
-
-// API keyword, but needs special handling by some templates
-define NULL_HANDLE 0
-
-// 1
-@extension("VK_KHR_surface") define VK_KHR_SURFACE_SPEC_VERSION                 25
-@extension("VK_KHR_surface") define VK_KHR_SURFACE_EXTENSION_NAME               "VK_KHR_surface"
-
-// 2
-@extension("VK_KHR_swapchain") define VK_KHR_SWAPCHAIN_SPEC_VERSION             70
-@extension("VK_KHR_swapchain") define VK_KHR_SWAPCHAIN_EXTENSION_NAME           "VK_KHR_swapchain"
-
-// 3
-@extension("VK_KHR_display") define VK_KHR_DISPLAY_SPEC_VERSION                 21
-@extension("VK_KHR_display") define VK_KHR_DISPLAY_EXTENSION_NAME               "VK_KHR_display"
-
-// 4
-@extension("VK_KHR_display_swapchain") define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION     9
-@extension("VK_KHR_display_swapchain") define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME   "VK_KHR_display_swapchain"
-
-// 5
-@extension("VK_KHR_xlib_surface") define VK_KHR_XLIB_SURFACE_SPEC_VERSION       6
-@extension("VK_KHR_xlib_surface") define VK_KHR_XLIB_SURFACE_NAME               "VK_KHR_xlib_surface"
-
-// 6
-@extension("VK_KHR_xcb_surface") define VK_KHR_XCB_SURFACE_SPEC_VERSION         6
-@extension("VK_KHR_xcb_surface") define VK_KHR_XCB_SURFACE_NAME                 "VK_KHR_xcb_surface"
-
-// 7
-@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6
-@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_NAME         "VK_KHR_wayland_surface"
-
-// 8 - VK_KHR_mir_surface removed
-
-// 9
-@extension("VK_KHR_android_surface") define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6
-@extension("VK_KHR_android_surface") define VK_KHR_ANDROID_SURFACE_NAME         "VK_KHR_android_surface"
-
-// 10
-@extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION     6
-@extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME             "VK_KHR_win32_surface"
-
-// 11
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION     8
-@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME             "VK_ANDROID_native_buffer"
-
-// 12
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION       9
-@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME               "VK_EXT_debug_report"
-
-// 13
-@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION           1
-@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_NAME                   "VK_NV_glsl_shader"
-
-// 14
-@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION   1
-@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_NAME           "VK_EXT_depth_range_unrestricted"
-
-// 15
-@extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION   1
-@extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_NAME           "VK_KHR_sampler_mirror_clamp_to_edge"
-
-// 16
-@extension("VK_IMG_filter_cubic") define VK_IMG_FILTER_CUBIC_SPEC_VERSION       1
-@extension("VK_IMG_filter_cubic") define VK_IMG_FILTER_CUBIC_NAME               "VK_IMG_filter_cubic"
-
-// 19
-@extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION   1
-@extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_NAME           "VK_AMD_rasterization_order"
-
-// 21
-@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1
-@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax"
-
-// 22
-@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1
-@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter"
-
-// 23
-@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION       4
-@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_NAME               "VK_EXT_debug_marker"
-
-// 26
-@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_SPEC_VERSION 1
-@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader"
-
-// 27
-@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1
-@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation"
-
-// 28
-@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1
-@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc"
-
-// 29
-@extension("VK_EXT_transform_feedback") define VK_EXT_TRANSFORM_FEEDBACK_SPEC_VERSION 1
-@extension("VK_EXT_transform_feedback") define VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME "VK_EXT_transform_feedback"
-
-// 34
-@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
-@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count"
-
-// 36
-@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1
-@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height"
-
-// 37
-@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1
-@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float"
-
-// 38
-@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1
-@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot"
-
-// 42
-@extension("VK_AMD_texture_gather_bias_lod") define VK_AMD_TEXTURE_GATHER_BIAS_LOD_SPEC_VERSION 1
-@extension("VK_AMD_texture_gather_bias_lod") define VK_AMD_TEXTURE_GATHER_BIAS_LOD_EXTENSION_NAME "VK_AMD_texture_gather_bias_lod"
-
-// 43
-@extension("VK_AMD_shader_info") define VK_AMD_SHADER_INFO_SPEC_VERSION 1
-@extension("VK_AMD_shader_info") define VK_AMD_SHADER_INFO_EXTENSION_NAME "VK_AMD_shader_info"
-
-// 47
-@extension("VK_AMD_shader_image_load_store_lod") define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_SPEC_VERSION 1
-@extension("VK_AMD_shader_image_load_store_lod") define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME "VK_AMD_shader_image_load_store_lod"
-
-// 51
-@extension("VK_NV_corner_sampled_image") define VK_NV_CORNER_SAMPLED_IMAGE_SPEC_VERSION 2
-@extension("VK_NV_corner_sampled_image") define VK_NV_CORNER_SAMPLED_IMAGE_EXTENSION_NAME "VK_NV_corner_sampled_image"
-
-// 54
-@extension("VK_KHR_multiview") define VK_KHR_MULTIVIEW_SPEC_VERSION 1
-@extension("VK_KHR_multiview") define VK_KHR_MULTIVIEW_EXTENSION_NAME "VK_KHR_multiview"
-
-// 56
-@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
-@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities"
-
-// 57
-@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1
-@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory"
-
-// 58
-@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
-@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32"
-
-// 59
-@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1
-@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex"
-
-// 60
-@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1
-@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2"
-
-// 61
-@extension("VK_KHR_device_group") define VK_KHR_DEVICE_GROUP_SPEC_VERSION 3
-@extension("VK_KHR_device_group") define VK_KHR_DEVICE_GROUP_EXTENSION_NAME "VK_KHR_device_group"
-
-// 62
-@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1
-@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags"
-
-// 63
-@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_SPEC_VERSION 1
-@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface"
-
-// 64
-@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1
-@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters"
-
-// 65
-@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1
-@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot"
-
-// 66
-@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1
-@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote"
-
-// 68
-@extension("VK_EXT_astc_decode_mode") define VK_EXT_ASTC_DECODE_MODE_SPEC_VERSION 1
-@extension("VK_EXT_astc_decode_mode") define VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME "VK_EXT_astc_decode_mode"
-
-// 70
-@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_SPEC_VERSION 2
-@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1"
-
-// 71
-@extension("VK_KHR_device_group_creation") define VK_KHR_DEVICE_GROUP_CREATION_SPEC_VERSION 1
-@extension("VK_KHR_device_group_creation") define VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHR_device_group_creation"
-
-// 72
-@extension("VK_KHR_external_memory_capabilities") define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1
-@extension("VK_KHR_external_memory_capabilities") define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_memory_capabilities"
-
-// 73
-@extension("VK_KHR_external_memory") define VK_KHR_EXTERNAL_MEMORY_SPEC_VERSION 1
-@extension("VK_KHR_external_memory") define VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHR_external_memory"
-
-// 74
-@extension("VK_KHR_external_memory_win32") define VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1
-@extension("VK_KHR_external_memory_win32") define VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHR_external_memory_win32"
-
-// 75
-@extension("VK_KHR_external_memory_fd") define VK_KHR_EXTERNAL_MEMORY_FD_SPEC_VERSION 1
-@extension("VK_KHR_external_memory_fd") define VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHR_external_memory_fd"
-
-// 76
-@extension("VK_KHR_win32_keyed_mutex") define VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION 1
-@extension("VK_KHR_win32_keyed_mutex") define VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHR_win32_keyed_mutex"
-
-// 77
-@extension("VK_KHR_external_semaphore_capabilities") define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1
-@extension("VK_KHR_external_semaphore_capabilities") define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_semaphore_capabilities"
-
-// 78
-@extension("VK_KHR_external_semaphore") define VK_KHR_EXTERNAL_SEMAPHORE_SPEC_VERSION 1
-@extension("VK_KHR_external_semaphore") define VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHR_external_semaphore"
-
-// 79
-@extension("VK_KHR_external_semaphore_win32") define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1
-@extension("VK_KHR_external_semaphore_win32") define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHR_external_semaphore_win32"
-
-// 80
-@extension("VK_KHR_external_semaphore_fd") define VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1
-@extension("VK_KHR_external_semaphore_fd") define VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHR_external_semaphore_fd"
-
-// 81
-@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 2
-@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor"
-
-// 82
-@extension("VK_EXT_conditional_rendering") define VK_EXT_CONDITIONAL_RENDERING_SPEC_VERSION 1
-@extension("VK_EXT_conditional_rendering") define VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME "VK_EXT_conditional_rendering"
-
-// 83
-@extension("VK_KHR_shader_float16_int8") define VK_KHR_SHADER_FLOAT16_INT8_SPEC_VERSION 1
-@extension("VK_KHR_shader_float16_int8") define VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME "VK_KHR_shader_float16_int8"
-
-// 84
-@extension("VK_KHR_16bit_storage") define VK_KHR_16BIT_STORAGE_SPEC_VERSION 1
-@extension("VK_KHR_16bit_storage") define VK_KHR_16BIT_STORAGE_EXTENSION_NAME "VK_KHR_16bit_storage"
-
-// 85
-@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
-@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"
-
-// 86
-@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1
-@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template"
-
-// 87
-@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3
-@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands"
-
-// 88
-@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1
-@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling"
-
-// 89
-@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1
-@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display"
-
-// 90
-@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1
-@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display"
-
-// 91
-@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1
-@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter"
-
-// 92
-@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1
-@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_COUNTER_EXTENSION_NAME "VK_EXT_display_control"
-
-// 93
-@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1
-@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing"
-
-// 95
-@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1
-@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage"
-
-// 96
-@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1
-@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough"
-
-// 97
-@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1
-@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2"
-
-// 98
-@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1
-@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes"
-
-// 99
-@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1
-@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle"
-
-// 100
-@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1
-@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles"
-
-// 102
-@extension("VK_EXT_conservative_rasterization") define VK_EXT_CONSERVATIVE_RASTERIZATION_SPEC_VERSION 1
-@extension("VK_EXT_conservative_rasterization") define VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME "VK_EXT_conservative_rasterization"
-
-// 105
-@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 3
-@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace"
-
-// 106
-@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_SPEC_VERSION 1
-@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata"
-
-// 110
-@extension("VK_KHR_create_renderpass2") define VK_KHR_CREATE_RENDERPASS2_SPEC_VERSION 1
-@extension("VK_KHR_create_renderpass2") define VK_KHR_CREATE_RENDERPASS2_EXTENSION_NAME "VK_KHR_create_renderpass2"
-
-// 112
-@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1
-@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image"
-
-// 113
-@extension("VK_KHR_external_fence_capabilities") define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_SPEC_VERSION 1
-@extension("VK_KHR_external_fence_capabilities") define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_fence_capabilities"
-
-// 114
-@extension("VK_KHR_external_fence") define VK_KHR_EXTERNAL_FENCE_SPEC_VERSION 1
-@extension("VK_KHR_external_fence") define VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME "VK_KHR_external_fence"
-
-// 115
-@extension("VK_KHR_external_fence_win32") define VK_KHR_EXTERNAL_FENCE_WIN32_SPEC_VERSION 1
-@extension("VK_KHR_external_fence_win32") define VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME "VK_KHR_external_fence_win32"
-
-// 116
-@extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_SPEC_VERSION 1
-@extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME "VK_KHR_external_fence_fd"
-
-// 118
-@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_SPEC_VERSION 1
-@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2"
-
-// 120
-@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1
-@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2"
-
-// 121
-@extension("VK_KHR_variable_pointers") define VK_KHR_VARIABLE_POINTERS_SPEC_VERSION 1
-@extension("VK_KHR_variable_pointers") define VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME "VK_KHR_variable_pointers"
-
-// 122
-@extension("VK_KHR_get_display_properties2") define VK_KHR_GET_DISPLAY_PROPERTIES_2_SPEC_VERSION 1
-@extension("VK_KHR_get_display_properties2") define VK_KHR_GET_DISPLAY_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_display_properties2"
-
-// 123
-@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_SPEC_VERSION 1
-@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface"
-
-// 124
-@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_SPEC_VERSION 1
-@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface"
-
-// 126
-@extension("VK_EXT_external_memory_dma_buf") define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_SPEC_VERSION 1
-@extension("VK_EXT_external_memory_dma_buf") define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME "VK_EXT_external_memory_dma_buf"
-
-// 127
-@extension("VK_EXT_queue_family_foreign") define VK_EXT_QUEUE_FAMILY_FOREIGN_SPEC_VERSION 1
-@extension("VK_EXT_queue_family_foreign") define VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME "VK_EXT_queue_family_foreign"
-
-// 128
-@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3
-@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation"
-
-// 128
-@extension("VK_EXT_debug_utils") define VK_EXT_DEBUG_UTILS_SPEC_VERSION 1
-@extension("VK_EXT_debug_utils") define VK_EXT_DEBUG_UTILS_EXTENSION_NAME "VK_EXT_debug_utils"
-
-// 130
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION 3
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME "VK_ANDROID_external_memory_android_hardware_buffer"
-
-// 131
-@extension("VK_EXT_sampler_filter_minmax") define VK_EXT_SAMPLER_FILTER_MINMAX_SPEC_VERSION 1
-@extension("VK_EXT_sampler_filter_minmax") define VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME "VK_EXT_sampler_filter_minmax"
-
-// 132
-@extension("VK_KHR_storage_buffer_storage_class") define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_SPEC_VERSION 1
-@extension("VK_KHR_storage_buffer_storage_class") define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME "VK_KHR_storage_buffer_storage_class"
-
-// 133
-@extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 1
-@extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16"
-
-// 137
-@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1
-@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples"
-
-// 138
-@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1
-@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask"
-
-// 139
-@extension("VK_EXT_inline_uniform_block") define VK_EXT_INLINE_UNIFORM_BLOCK_SPEC_VERSION 1
-@extension("VK_EXT_inline_uniform_block") define VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME "VK_EXT_inline_uniform_block"
-
-// 141
-@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1
-@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export"
-
-// 144
-@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1
-@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations"
-
-// 145
-@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1
-@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout"
-
-// 147
-@extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_SPEC_VERSION 1
-@extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_EXTENSION_NAME "VK_KHR_get_memory_requirements2"
-
-// 148
-@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1
-@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list"
-
-// 149
-@extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2
-@extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced"
-
-// 150
-@extension("VK_NV_fragment_coverage_to_color") define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_SPEC_VERSION 1
-@extension("VK_NV_fragment_coverage_to_color") define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_EXTENSION_NAME "VK_NV_fragment_coverage_to_color"
-
-// 153
-@extension("VK_NV_framebuffer_mixed_samples") define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_SPEC_VERSION 1
-@extension("VK_NV_framebuffer_mixed_samples") define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_EXTENSION_NAME "VK_NV_framebuffer_mixed_samples"
-
-// 154
-@extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_SPEC_VERSION 1
-@extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle"
-
-// 156
-@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1
-@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage"
-
-// 157
-@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1
-@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion"
-
-// 158
-@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_SPEC_VERSION 1
-@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_EXTENSION_NAME "VK_KHR_bind_memory2"
-
-// 159
-@extension("VK_EXT_image_drm_format_modifier") define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION 1
-@extension("VK_EXT_image_drm_format_modifier") define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME "VK_EXT_image_drm_format_modifier"
-
-// 161
-@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1
-@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache"
-
-// 162
-@extension("VK_EXT_descriptor_indexing") define VK_EXT_DESCRIPTOR_INDEXING_SPEC_VERSION 2
-@extension("VK_EXT_descriptor_indexing") define VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME "VK_EXT_descriptor_indexing"
-
-// 163
-@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1
-@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer"
-
-// 165
-@extension("VK_NV_shading_rate_image") define VK_NV_SHADING_RATE_IMAGE_SPEC_VERSION 3
-@extension("VK_NV_shading_rate_image") define VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME "VK_NV_shading_rate_image"
-
-// 166
-@extension("VK_NV_ray_tracing") define VK_NV_RAY_TRACING_SPEC_VERSION 3
-@extension("VK_NV_ray_tracing") define VK_NV_RAY_TRACING_EXTENSION_NAME "VK_NV_ray_tracing"
-
-// 167
-@extension("VK_NV_representative_fragment_test") define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_SPEC_VERSION 1
-@extension("VK_NV_representative_fragment_test") define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_EXTENSION_NAME "VK_NV_representative_fragment_test"
-
-// 169
-@extension("VK_KHR_maintenance3") define VK_KHR_MAINTENANCE3_SPEC_VERSION 1
-@extension("VK_KHR_maintenance3") define VK_KHR_MAINTENANCE3_EXTENSION_NAME "VK_KHR_maintenance3"
-
-// 170
-@extension("VK_KHR_draw_indirect_count") define VK_KHR_DRAW_INDIRECT_COUNT_SPEC_VERSION 1
-@extension("VK_KHR_draw_indirect_count") define VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_KHR_draw_indirect_count"
-
-// 175
-@extension("VK_EXT_global_priority") define VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION 1
-@extension("VK_EXT_global_priority") define VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME "VK_EXT_global_priority"
-
-// 178
-@extension("VK_KHR_8bit_storage") define VK_KHR_8BIT_STORAGE_SPEC_VERSION 1
-@extension("VK_KHR_8bit_storage") define VK_KHR_8BIT_STORAGE_EXTENSION_NAME "VK_KHR_8bit_storage"
-
-// 179
-@extension("VK_EXT_external_memory_host") define VK_EXT_EXTERNAL_MEMORY_HOST_SPEC_VERSION 1
-@extension("VK_EXT_external_memory_host") define VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME "VK_EXT_external_memory_host"
-
-// 180
-@extension("VK_AMD_buffer_marker") define VK_AMD_BUFFER_MARKER_SPEC_VERSION 1
-@extension("VK_AMD_buffer_marker") define VK_AMD_BUFFER_MARKER_EXTENSION_NAME "VK_AMD_buffer_marker"
-
-// 181
-@extension("VK_KHR_shader_atomic_int64") define VK_KHR_SHADER_ATOMIC_INT64_SPEC_VERSION 1
-@extension("VK_KHR_shader_atomic_int64") define VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME "VK_KHR_shader_atomic_int64"
-
-// 186
-@extension("VK_AMD_shader_core_properties") define VK_AMD_SHADER_CORE_PROPERTIES_SPEC_VERSION 1
-@extension("VK_AMD_shader_core_properties") define VK_AMD_SHADER_CORE_PROPERTIES_EXTENSION_NAME "VK_AMD_shader_core_properties"
-
-// 190
-@extension("VK_AMD_memory_overallocation_behavior") define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_SPEC_VERSION 1
-@extension("VK_AMD_memory_overallocation_behavior") define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_EXTENSION_NAME "VK_AMD_memory_overallocation_behavior"
-
-// 191
-@extension("VK_EXT_vertex_attribute_divisor") define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 2
-@extension("VK_EXT_vertex_attribute_divisor") define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor"
-
-// 197
-@extension("VK_KHR_driver_properties") define VK_KHR_DRIVER_PROPERTIES_SPEC_VERSION 1
-@extension("VK_KHR_driver_properties") define VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME "VK_KHR_driver_properties"
-
-// 198
-@extension("VK_KHR_shader_float_controls") define VK_KHR_SHADER_FLOAT_CONTROLS_SPEC_VERSION 1
-@extension("VK_KHR_shader_float_controls") define VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME "VK_KHR_shader_float_controls"
-
-// 199
-@extension("VK_NV_shader_subgroup_partitioned") define VK_NV_SHADER_SUBGROUP_PARTITIONED_SPEC_VERSION 1
-@extension("VK_NV_shader_subgroup_partitioned") define VK_NV_SHADER_SUBGROUP_PARTITIONED_EXTENSION_NAME "VK_NV_shader_subgroup_partitioned"
-
-// 201
-@extension("VK_KHR_swapchain_mutable_format") define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION 1
-@extension("VK_KHR_swapchain_mutable_format") define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME "VK_KHR_swapchain_mutable_format"
-
-// 202
-@extension("VK_NV_compute_shader_derivatives") define VK_NV_COMPUTE_SHADER_DERIVATIVES_SPEC_VERSION 1
-@extension("VK_NV_compute_shader_derivatives") define VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME "VK_NV_compute_shader_derivatives"
-
-// 203
-@extension("VK_NV_mesh_shader") define VK_NV_MESH_SHADER_SPEC_VERSION 1
-@extension("VK_NV_mesh_shader") define VK_NV_MESH_SHADER_EXTENSION_NAME "VK_NV_mesh_shader"
-
-// 204
-@extension("VK_NV_fragment_shader_barycentric") define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_SPEC_VERSION 1
-@extension("VK_NV_fragment_shader_barycentric") define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME "VK_NV_fragment_shader_barycentric"
-
-// 205
-@extension("VK_NV_shader_image_footprint") define VK_NV_SHADER_IMAGE_FOOTPRINT_SPEC_VERSION 1
-@extension("VK_NV_shader_image_footprint") define VK_NV_SHADER_IMAGE_FOOTPRINT_EXTENSION_NAME "VK_NV_shader_image_footprint"
-
-// 206
-@extension("VK_NV_scissor_exclusive") define VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION 1
-@extension("VK_NV_scissor_exclusive") define VK_NV_SCISSOR_EXCLUSIVE_EXTENSION_NAME "VK_NV_scissor_exclusive"
-
-// 207
-@extension("VK_NV_device_diagnostic_checkpoints") define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_SPEC_VERSION 2
-@extension("VK_NV_device_diagnostic_checkpoints") define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME "VK_NV_device_diagnostic_checkpoints"
-
-// 212
-@extension("VK_KHR_vulkan_memory_model") define VK_KHR_VULKAN_MEMORY_MODEL_SPEC_VERSION 2
-@extension("VK_KHR_vulkan_memory_model") define VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME "VK_KHR_vulkan_memory_model"
-
-// 213
-@extension("VK_EXT_pci_bus_info") define VK_EXT_PCI_BUS_INFO_SPEC_VERSION 2
-@extension("VK_EXT_pci_bus_info") define VK_EXT_PCI_BUS_INFO_EXENSION_NAME "VK_EXT_pci_bus_info"
-
-// 215
-@extension("VK_FUCHSIA_imagepipe_surface") define VK_FUCHSIA_IMAGEPIPE_SURFACE_SPEC_VERSION 1
-@extension("VK_FUCHSIA_imagepipe_surface") define VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME "VK_FUCHSIA_imagepipe_surface"
-
-// 219
-@extension("VK_EXT_fragment_density_map") define VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION 1
-@extension("VK_EXT_fragment_density_map") define VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME "VK_EXT_fragment_density_map"
-
-// 222
-@extension("VK_EXT_scalar_block_layout") define VK_EXT_SCALAR_BLOCK_LAYOUT_SPEC_VERSION 1
-@extension("VK_EXT_scalar_block_layout") define VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME "VK_EXT_scalar_block_layout"
-
-// 224
-@extension("VK_GOOGLE_hlsl_functionality1") define VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION 1
-@extension("VK_GOOGLE_hlsl_functionality1") define VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME "VK_GOOGLE_hlsl_functionality1"
-
-// 225
-@extension("VK_GOOGLE_decorate_string") define VK_GOOGLE_DECORATE_STRING_SPEC_VERSION 1
-@extension("VK_GOOGLE_decorate_string") define VK_GOOGLE_DECORATE_STRING_EXTENSION_NAME "VK_GOOGLE_decorate_string"
-
-// 247
-@extension("VK_EXT_separate_stencil_usage") define VK_EXT_SEPARATE_STENCIL_USAGE_SPEC_VERSION 1
-@extension("VK_EXT_separate_stencil_usage") define VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME "VK_EXT_separate_stencil_usage"
-
-/////////////
-//  Types  //
-/////////////
-
-type u32 VkBool32
-type u32 VkFlags
-type u64 VkDeviceSize
-type u32 VkSampleMask
-
-/// Dispatchable handle types.
-@dispatchHandle type u64 VkInstance
-@dispatchHandle type u64 VkPhysicalDevice
-@dispatchHandle type u64 VkDevice
-@dispatchHandle type u64 VkQueue
-@dispatchHandle type u64 VkCommandBuffer
-
-/// Non dispatchable handle types.
-@nonDispatchHandle type u64 VkDeviceMemory
-@nonDispatchHandle type u64 VkCommandPool
-@nonDispatchHandle type u64 VkBuffer
-@nonDispatchHandle type u64 VkBufferView
-@nonDispatchHandle type u64 VkImage
-@nonDispatchHandle type u64 VkImageView
-@nonDispatchHandle type u64 VkShaderModule
-@nonDispatchHandle type u64 VkPipeline
-@nonDispatchHandle type u64 VkPipelineLayout
-@nonDispatchHandle type u64 VkSampler
-@nonDispatchHandle type u64 VkDescriptorSet
-@nonDispatchHandle type u64 VkDescriptorSetLayout
-@nonDispatchHandle type u64 VkDescriptorPool
-@nonDispatchHandle type u64 VkFence
-@nonDispatchHandle type u64 VkSemaphore
-@nonDispatchHandle type u64 VkEvent
-@nonDispatchHandle type u64 VkQueryPool
-@nonDispatchHandle type u64 VkFramebuffer
-@nonDispatchHandle type u64 VkRenderPass
-@nonDispatchHandle type u64 VkPipelineCache
-
-@vulkan1_1
-@nonDispatchHandle type u64 VkSamplerYcbcrConversion
-@nonDispatchHandle type u64 VkDescriptorUpdateTemplate
-
-// 1
-@extension("VK_KHR_surface")    @nonDispatchHandle type u64 VkSurfaceKHR
-
-// 2
-@extension("VK_KHR_swapchain")  @nonDispatchHandle type u64 VkSwapchainKHR
-
-// 3
-@extension("VK_KHR_display")    @nonDispatchHandle type u64 VkDisplayKHR
-@extension("VK_KHR_display")    @nonDispatchHandle type u64 VkDisplayModeKHR
-
-// 12
-@extension("VK_EXT_debug_report") @nonDispatchHandle type u64 VkDebugReportCallbackEXT
-
-// 86
-@extension("VK_KHR_descriptor_update_template") @nonDispatchHandle type u64 VkDescriptorUpdateTemplateKHR
-
-// 87
-@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX
-@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX
-
-// 129
-@extension("VK_EXT_debug_utils") @nonDispatchHandle type u64 VkDebugUtilsMessengerEXT
-
-// 157
-@extension("VK_KHR_sampler_ycbcr_conversion") @nonDispatchHandle type u64 VkSamplerYcbcrConversionKHR
-
-// 161
-@extension("VK_EXT_validation_cache") @nonDispatchHandle type u64 VkValidationCacheEXT
-
-// 166
-@extension("VK_NV_ray_tracing") @nonDispatchHandle type u64 VkAccelerationStructureNV
-
-/////////////
-//  Enums  //
-/////////////
-
-enum VkImageLayout {
-    VK_IMAGE_LAYOUT_UNDEFINED                               = 0x00000000,   /// Implicit layout an image is when its contents are undefined due to various reasons (e.g. right after creation)
-    VK_IMAGE_LAYOUT_GENERAL                                 = 0x00000001,   /// General layout when image can be used for any kind of access
-    VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                = 0x00000002,   /// Optimal layout when image is only used for color attachment read/write
-    VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL        = 0x00000003,   /// Optimal layout when image is only used for depth/stencil attachment read/write
-    VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL         = 0x00000004,   /// Optimal layout when image is used for read only depth/stencil attachment and shader access
-    VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL                = 0x00000005,   /// Optimal layout when image is used for read only shader access
-    VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL                    = 0x00000006,   /// Optimal layout when image is used only as source of transfer operations
-    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL                    = 0x00000007,   /// Optimal layout when image is used only as destination of transfer operations
-    VK_IMAGE_LAYOUT_PREINITIALIZED                          = 0x00000008,   /// Initial layout used when the data is populated by the CPU
-
-    //@vulkan1_1
-    VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL = 1000117000,
-    VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL = 1000117001,
-
-    //@extension("VK_KHR_swapchain") // 2
-    VK_IMAGE_LAYOUT_PRESENT_SRC_KHR                         = 1000001002,
-
-    //@extension("VK_KHR_shared_presentable_image") // 112
-    VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR                      = 1000111000,
-
-    //@extension("VK_KHR_maintenance2") // 118
-    VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR  = 1000117000,
-    VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR  = 1000117001,
-
-    //@extension("VK_NV_shading_rate_image") // 165
-    VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV                 = 1000164003,
-
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT        = 1000218000,
-}
-
-enum VkAttachmentLoadOp {
-    VK_ATTACHMENT_LOAD_OP_LOAD                              = 0x00000000,
-    VK_ATTACHMENT_LOAD_OP_CLEAR                             = 0x00000001,
-    VK_ATTACHMENT_LOAD_OP_DONT_CARE                         = 0x00000002,
-}
-
-enum VkAttachmentStoreOp {
-    VK_ATTACHMENT_STORE_OP_STORE                            = 0x00000000,
-    VK_ATTACHMENT_STORE_OP_DONT_CARE                        = 0x00000001,
-}
-
-enum VkImageType {
-    VK_IMAGE_TYPE_1D                                        = 0x00000000,
-    VK_IMAGE_TYPE_2D                                        = 0x00000001,
-    VK_IMAGE_TYPE_3D                                        = 0x00000002,
-}
-
-enum VkImageTiling {
-    VK_IMAGE_TILING_OPTIMAL                                 = 0x00000000,
-    VK_IMAGE_TILING_LINEAR                                  = 0x00000001,
-
-    //@extension("VK_EXT_image_drm_format_modifier") // 159
-    VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT                 = 1000158000,
-}
-
-enum VkImageViewType {
-    VK_IMAGE_VIEW_TYPE_1D                                   = 0x00000000,
-    VK_IMAGE_VIEW_TYPE_2D                                   = 0x00000001,
-    VK_IMAGE_VIEW_TYPE_3D                                   = 0x00000002,
-    VK_IMAGE_VIEW_TYPE_CUBE                                 = 0x00000003,
-    VK_IMAGE_VIEW_TYPE_1D_ARRAY                             = 0x00000004,
-    VK_IMAGE_VIEW_TYPE_2D_ARRAY                             = 0x00000005,
-    VK_IMAGE_VIEW_TYPE_CUBE_ARRAY                           = 0x00000006,
-}
-
-enum VkCommandBufferLevel {
-    VK_COMMAND_BUFFER_LEVEL_PRIMARY                         = 0x00000000,
-    VK_COMMAND_BUFFER_LEVEL_SECONDARY                       = 0x00000001,
-}
-
-enum VkComponentSwizzle {
-    VK_COMPONENT_SWIZZLE_IDENTITY                           = 0x00000000,
-    VK_COMPONENT_SWIZZLE_ZERO                               = 0x00000001,
-    VK_COMPONENT_SWIZZLE_ONE                                = 0x00000002,
-    VK_COMPONENT_SWIZZLE_R                                  = 0x00000003,
-    VK_COMPONENT_SWIZZLE_G                                  = 0x00000004,
-    VK_COMPONENT_SWIZZLE_B                                  = 0x00000005,
-    VK_COMPONENT_SWIZZLE_A                                  = 0x00000006,
-}
-
-enum VkDescriptorType {
-    VK_DESCRIPTOR_TYPE_SAMPLER                              = 0x00000000,
-    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER               = 0x00000001,
-    VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE                        = 0x00000002,
-    VK_DESCRIPTOR_TYPE_STORAGE_IMAGE                        = 0x00000003,
-    VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER                 = 0x00000004,
-    VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER                 = 0x00000005,
-    VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER                       = 0x00000006,
-    VK_DESCRIPTOR_TYPE_STORAGE_BUFFER                       = 0x00000007,
-    VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC               = 0x00000008,
-    VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC               = 0x00000009,
-    VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT                     = 0x0000000a,
-
-    //@extension("VK_EXT_inline_uniform_block") // 139
-    VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT             = 1000138000,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV            = 1000165000,
-}
-
-enum VkQueryType {
-    VK_QUERY_TYPE_OCCLUSION                                 = 0x00000000,
-    VK_QUERY_TYPE_PIPELINE_STATISTICS                       = 0x00000001, /// Optional
-    VK_QUERY_TYPE_TIMESTAMP                                 = 0x00000002,
-
-    //@extension("VK_EXT_transform_feedback") // 29
-    VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT             = 1000028004,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV  = 1000165000,
-}
-
-enum VkBorderColor {
-    VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK                 = 0x00000000,
-    VK_BORDER_COLOR_INT_TRANSPARENT_BLACK                   = 0x00000001,
-    VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK                      = 0x00000002,
-    VK_BORDER_COLOR_INT_OPAQUE_BLACK                        = 0x00000003,
-    VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE                      = 0x00000004,
-    VK_BORDER_COLOR_INT_OPAQUE_WHITE                        = 0x00000005,
-}
-
-enum VkPipelineBindPoint {
-    VK_PIPELINE_BIND_POINT_GRAPHICS                         = 0x00000000,
-    VK_PIPELINE_BIND_POINT_COMPUTE                          = 0x00000001,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_PIPELINE_BIND_POINT_RAY_TRACING_NV                   = 1000165000,
-}
-
-enum VkPrimitiveTopology {
-    VK_PRIMITIVE_TOPOLOGY_POINT_LIST                        = 0x00000000,
-    VK_PRIMITIVE_TOPOLOGY_LINE_LIST                         = 0x00000001,
-    VK_PRIMITIVE_TOPOLOGY_LINE_STRIP                        = 0x00000002,
-    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST                     = 0x00000003,
-    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP                    = 0x00000004,
-    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN                      = 0x00000005,
-    VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY          = 0x00000006,
-    VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY         = 0x00000007,
-    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY      = 0x00000008,
-    VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY     = 0x00000009,
-    VK_PRIMITIVE_TOPOLOGY_PATCH_LIST                        = 0x0000000a,
-}
-
-enum VkSharingMode {
-    VK_SHARING_MODE_EXCLUSIVE                               = 0x00000000,
-    VK_SHARING_MODE_CONCURRENT                              = 0x00000001,
-}
-
-enum VkIndexType {
-    VK_INDEX_TYPE_UINT16                                    = 0x00000000,
-    VK_INDEX_TYPE_UINT32                                    = 0x00000001,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_INDEX_TYPE_NONE_NV                                   = 1000165000,
-}
-
-enum VkFilter {
-    VK_FILTER_NEAREST                                       = 0x00000000,
-    VK_FILTER_LINEAR                                        = 0x00000001,
-
-    //@extension("VK_IMG_filter_cubic") // 16
-    VK_FILTER_CUBIC_IMG                                     = 1000015000,
-}
-
-enum VkSamplerMipmapMode {
-    VK_SAMPLER_MIPMAP_MODE_NEAREST                          = 0x00000001,   /// Choose nearest mip level
-    VK_SAMPLER_MIPMAP_MODE_LINEAR                           = 0x00000002,   /// Linear filter between mip levels
-}
-
-enum VkSamplerAddressMode {
-    VK_SAMPLER_ADDRESS_MODE_REPEAT                          = 0x00000000,
-    VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT                 = 0x00000001,
-    VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE                   = 0x00000002,
-    VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER                 = 0x00000003,
-    VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE            = 0x00000004,
-}
-
-enum VkCompareOp {
-    VK_COMPARE_OP_NEVER                                     = 0x00000000,
-    VK_COMPARE_OP_LESS                                      = 0x00000001,
-    VK_COMPARE_OP_EQUAL                                     = 0x00000002,
-    VK_COMPARE_OP_LESS_OR_EQUAL                             = 0x00000003,
-    VK_COMPARE_OP_GREATER                                   = 0x00000004,
-    VK_COMPARE_OP_NOT_EQUAL                                 = 0x00000005,
-    VK_COMPARE_OP_GREATER_OR_EQUAL                          = 0x00000006,
-    VK_COMPARE_OP_ALWAYS                                    = 0x00000007,
-}
-
-enum VkPolygonMode {
-    VK_POLYGON_MODE_FILL                                    = 0x00000000,
-    VK_POLYGON_MODE_LINE                                    = 0x00000001,
-    VK_POLYGON_MODE_POINT                                   = 0x00000002,
-
-    //@extension("VK_NV_fill_rectangle") // 154
-    VK_POLYGON_MODE_FILL_RECTANGLE_NV                       = 1000153000,
-}
-
-enum VkFrontFace {
-    VK_FRONT_FACE_COUNTER_CLOCKWISE                         = 0x00000000,
-    VK_FRONT_FACE_CLOCKWISE                                 = 0x00000001,
-}
-
-enum VkBlendFactor {
-    VK_BLEND_FACTOR_ZERO                                    = 0x00000000,
-    VK_BLEND_FACTOR_ONE                                     = 0x00000001,
-    VK_BLEND_FACTOR_SRC_COLOR                               = 0x00000002,
-    VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR                     = 0x00000003,
-    VK_BLEND_FACTOR_DST_COLOR                               = 0x00000004,
-    VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR                     = 0x00000005,
-    VK_BLEND_FACTOR_SRC_ALPHA                               = 0x00000006,
-    VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA                     = 0x00000007,
-    VK_BLEND_FACTOR_DST_ALPHA                               = 0x00000008,
-    VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA                     = 0x00000009,
-    VK_BLEND_FACTOR_CONSTANT_COLOR                          = 0x0000000a,
-    VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR                = 0x0000000b,
-    VK_BLEND_FACTOR_CONSTANT_ALPHA                          = 0x0000000c,
-    VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA                = 0x0000000d,
-    VK_BLEND_FACTOR_SRC_ALPHA_SATURATE                      = 0x0000000e,
-    VK_BLEND_FACTOR_SRC1_COLOR                              = 0x0000000f,
-    VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR                    = 0x00000010,
-    VK_BLEND_FACTOR_SRC1_ALPHA                              = 0x00000011,
-    VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA                    = 0x00000012,
-}
-
-enum VkBlendOp {
-    VK_BLEND_OP_ADD                                         = 0x00000000,
-    VK_BLEND_OP_SUBTRACT                                    = 0x00000001,
-    VK_BLEND_OP_REVERSE_SUBTRACT                            = 0x00000002,
-    VK_BLEND_OP_MIN                                         = 0x00000003,
-    VK_BLEND_OP_MAX                                         = 0x00000004,
-
-    //@extension("VK_EXT_blend_operation_advanced") // 149
-    VK_BLEND_OP_ZERO_EXT                                    = 1000148000,
-    VK_BLEND_OP_SRC_EXT                                     = 1000148001,
-    VK_BLEND_OP_DST_EXT                                     = 1000148002,
-    VK_BLEND_OP_SRC_OVER_EXT                                = 1000148003,
-    VK_BLEND_OP_DST_OVER_EXT                                = 1000148004,
-    VK_BLEND_OP_SRC_IN_EXT                                  = 1000148005,
-    VK_BLEND_OP_DST_IN_EXT                                  = 1000148006,
-    VK_BLEND_OP_SRC_OUT_EXT                                 = 1000148007,
-    VK_BLEND_OP_DST_OUT_EXT                                 = 1000148008,
-    VK_BLEND_OP_SRC_ATOP_EXT                                = 1000148009,
-    VK_BLEND_OP_DST_ATOP_EXT                                = 1000148010,
-    VK_BLEND_OP_XOR_EXT                                     = 1000148011,
-    VK_BLEND_OP_MULTIPLY_EXT                                = 1000148012,
-    VK_BLEND_OP_SCREEN_EXT                                  = 1000148013,
-    VK_BLEND_OP_OVERLAY_EXT                                 = 1000148014,
-    VK_BLEND_OP_DARKEN_EXT                                  = 1000148015,
-    VK_BLEND_OP_LIGHTEN_EXT                                 = 1000148016,
-    VK_BLEND_OP_COLORDODGE_EXT                              = 1000148017,
-    VK_BLEND_OP_COLORBURN_EXT                               = 1000148018,
-    VK_BLEND_OP_HARDLIGHT_EXT                               = 1000148019,
-    VK_BLEND_OP_SOFTLIGHT_EXT                               = 1000148020,
-    VK_BLEND_OP_DIFFERENCE_EXT                              = 1000148021,
-    VK_BLEND_OP_EXCLUSION_EXT                               = 1000148022,
-    VK_BLEND_OP_INVERT_EXT                                  = 1000148023,
-    VK_BLEND_OP_INVERT_RGB_EXT                              = 1000148024,
-    VK_BLEND_OP_LINEARDODGE_EXT                             = 1000148025,
-    VK_BLEND_OP_LINEARBURN_EXT                              = 1000148026,
-    VK_BLEND_OP_VIVIDLIGHT_EXT                              = 1000148027,
-    VK_BLEND_OP_LINEARLIGHT_EXT                             = 1000148028,
-    VK_BLEND_OP_PINLIGHT_EXT                                = 1000148029,
-    VK_BLEND_OP_HARDMIX_EXT                                 = 1000148030,
-    VK_BLEND_OP_HSL_HUE_EXT                                 = 1000148031,
-    VK_BLEND_OP_HSL_SATURATION_EXT                          = 1000148032,
-    VK_BLEND_OP_HSL_COLOR_EXT                               = 1000148033,
-    VK_BLEND_OP_HSL_LUMINOSITY_EXT                          = 1000148034,
-    VK_BLEND_OP_PLUS_EXT                                    = 1000148035,
-    VK_BLEND_OP_PLUS_CLAMPED_EXT                            = 1000148036,
-    VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT                      = 1000148037,
-    VK_BLEND_OP_PLUS_DARKER_EXT                             = 1000148038,
-    VK_BLEND_OP_MINUS_EXT                                   = 1000148039,
-    VK_BLEND_OP_MINUS_CLAMPED_EXT                           = 1000148040,
-    VK_BLEND_OP_CONTRAST_EXT                                = 1000148041,
-    VK_BLEND_OP_INVERT_OVG_EXT                              = 1000148042,
-    VK_BLEND_OP_RED_EXT                                     = 1000148043,
-    VK_BLEND_OP_GREEN_EXT                                   = 1000148044,
-    VK_BLEND_OP_BLUE_EXT                                    = 1000148045,
-}
-
-enum VkStencilOp {
-    VK_STENCIL_OP_KEEP                                      = 0x00000000,
-    VK_STENCIL_OP_ZERO                                      = 0x00000001,
-    VK_STENCIL_OP_REPLACE                                   = 0x00000002,
-    VK_STENCIL_OP_INCREMENT_AND_CLAMP                       = 0x00000003,
-    VK_STENCIL_OP_DECREMENT_AND_CLAMP                       = 0x00000004,
-    VK_STENCIL_OP_INVERT                                    = 0x00000005,
-    VK_STENCIL_OP_INCREMENT_AND_WRAP                        = 0x00000006,
-    VK_STENCIL_OP_DECREMENT_AND_WRAP                        = 0x00000007,
-}
-
-enum VkLogicOp {
-    VK_LOGIC_OP_CLEAR                                       = 0x00000000,
-    VK_LOGIC_OP_AND                                         = 0x00000001,
-    VK_LOGIC_OP_AND_REVERSE                                 = 0x00000002,
-    VK_LOGIC_OP_COPY                                        = 0x00000003,
-    VK_LOGIC_OP_AND_INVERTED                                = 0x00000004,
-    VK_LOGIC_OP_NO_OP                                       = 0x00000005,
-    VK_LOGIC_OP_XOR                                         = 0x00000006,
-    VK_LOGIC_OP_OR                                          = 0x00000007,
-    VK_LOGIC_OP_NOR                                         = 0x00000008,
-    VK_LOGIC_OP_EQUIVALENT                                  = 0x00000009,
-    VK_LOGIC_OP_INVERT                                      = 0x0000000a,
-    VK_LOGIC_OP_OR_REVERSE                                  = 0x0000000b,
-    VK_LOGIC_OP_COPY_INVERTED                               = 0x0000000c,
-    VK_LOGIC_OP_OR_INVERTED                                 = 0x0000000d,
-    VK_LOGIC_OP_NAND                                        = 0x0000000e,
-    VK_LOGIC_OP_SET                                         = 0x0000000f,
-}
-
-enum VkSystemAllocationScope {
-    VK_SYSTEM_ALLOCATION_SCOPE_COMMAND                      = 0x00000000,
-    VK_SYSTEM_ALLOCATION_SCOPE_OBJECT                       = 0x00000001,
-    VK_SYSTEM_ALLOCATION_SCOPE_CACHE                        = 0x00000002,
-    VK_SYSTEM_ALLOCATION_SCOPE_DEVICE                       = 0x00000003,
-    VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE                     = 0x00000004,
-}
-
-enum VkInternalAllocationType {
-    VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE                  = 0x00000000,
-}
-
-enum VkPhysicalDeviceType {
-    VK_PHYSICAL_DEVICE_TYPE_OTHER                           = 0x00000000,
-    VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU                  = 0x00000001,
-    VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU                    = 0x00000002,
-    VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU                     = 0x00000003,
-    VK_PHYSICAL_DEVICE_TYPE_CPU                             = 0x00000004,
-}
-
-enum VkVertexInputRate {
-    VK_VERTEX_INPUT_RATE_VERTEX                             = 0x00000000,
-    VK_VERTEX_INPUT_RATE_INSTANCE                           = 0x00000001,
-}
-
-/// Vulkan format definitions
-enum VkFormat {
-    VK_FORMAT_UNDEFINED                                     = 0,
-    VK_FORMAT_R4G4_UNORM_PACK8                              = 1,
-    VK_FORMAT_R4G4B4A4_UNORM_PACK16                         = 2,
-    VK_FORMAT_B4G4R4A4_UNORM_PACK16                         = 3,
-    VK_FORMAT_R5G6B5_UNORM_PACK16                           = 4,
-    VK_FORMAT_B5G6R5_UNORM_PACK16                           = 5,
-    VK_FORMAT_R5G5B5A1_UNORM_PACK16                         = 6,
-    VK_FORMAT_B5G5R5A1_UNORM_PACK16                         = 7,
-    VK_FORMAT_A1R5G5B5_UNORM_PACK16                         = 8,
-    VK_FORMAT_R8_UNORM                                      = 9,
-    VK_FORMAT_R8_SNORM                                      = 10,
-    VK_FORMAT_R8_USCALED                                    = 11,
-    VK_FORMAT_R8_SSCALED                                    = 12,
-    VK_FORMAT_R8_UINT                                       = 13,
-    VK_FORMAT_R8_SINT                                       = 14,
-    VK_FORMAT_R8_SRGB                                       = 15,
-    VK_FORMAT_R8G8_UNORM                                    = 16,
-    VK_FORMAT_R8G8_SNORM                                    = 17,
-    VK_FORMAT_R8G8_USCALED                                  = 18,
-    VK_FORMAT_R8G8_SSCALED                                  = 19,
-    VK_FORMAT_R8G8_UINT                                     = 20,
-    VK_FORMAT_R8G8_SINT                                     = 21,
-    VK_FORMAT_R8G8_SRGB                                     = 22,
-    VK_FORMAT_R8G8B8_UNORM                                  = 23,
-    VK_FORMAT_R8G8B8_SNORM                                  = 24,
-    VK_FORMAT_R8G8B8_USCALED                                = 25,
-    VK_FORMAT_R8G8B8_SSCALED                                = 26,
-    VK_FORMAT_R8G8B8_UINT                                   = 27,
-    VK_FORMAT_R8G8B8_SINT                                   = 28,
-    VK_FORMAT_R8G8B8_SRGB                                   = 29,
-    VK_FORMAT_B8G8R8_UNORM                                  = 30,
-    VK_FORMAT_B8G8R8_SNORM                                  = 31,
-    VK_FORMAT_B8G8R8_USCALED                                = 32,
-    VK_FORMAT_B8G8R8_SSCALED                                = 33,
-    VK_FORMAT_B8G8R8_UINT                                   = 34,
-    VK_FORMAT_B8G8R8_SINT                                   = 35,
-    VK_FORMAT_B8G8R8_SRGB                                   = 36,
-    VK_FORMAT_R8G8B8A8_UNORM                                = 37,
-    VK_FORMAT_R8G8B8A8_SNORM                                = 38,
-    VK_FORMAT_R8G8B8A8_USCALED                              = 39,
-    VK_FORMAT_R8G8B8A8_SSCALED                              = 40,
-    VK_FORMAT_R8G8B8A8_UINT                                 = 41,
-    VK_FORMAT_R8G8B8A8_SINT                                 = 42,
-    VK_FORMAT_R8G8B8A8_SRGB                                 = 43,
-    VK_FORMAT_B8G8R8A8_UNORM                                = 44,
-    VK_FORMAT_B8G8R8A8_SNORM                                = 45,
-    VK_FORMAT_B8G8R8A8_USCALED                              = 46,
-    VK_FORMAT_B8G8R8A8_SSCALED                              = 47,
-    VK_FORMAT_B8G8R8A8_UINT                                 = 48,
-    VK_FORMAT_B8G8R8A8_SINT                                 = 49,
-    VK_FORMAT_B8G8R8A8_SRGB                                 = 50,
-    VK_FORMAT_A8B8G8R8_UNORM_PACK32                         = 51,
-    VK_FORMAT_A8B8G8R8_SNORM_PACK32                         = 52,
-    VK_FORMAT_A8B8G8R8_USCALED_PACK32                       = 53,
-    VK_FORMAT_A8B8G8R8_SSCALED_PACK32                       = 54,
-    VK_FORMAT_A8B8G8R8_UINT_PACK32                          = 55,
-    VK_FORMAT_A8B8G8R8_SINT_PACK32                          = 56,
-    VK_FORMAT_A8B8G8R8_SRGB_PACK32                          = 57,
-    VK_FORMAT_A2R10G10B10_UNORM_PACK32                      = 58,
-    VK_FORMAT_A2R10G10B10_SNORM_PACK32                      = 59,
-    VK_FORMAT_A2R10G10B10_USCALED_PACK32                    = 60,
-    VK_FORMAT_A2R10G10B10_SSCALED_PACK32                    = 61,
-    VK_FORMAT_A2R10G10B10_UINT_PACK32                       = 62,
-    VK_FORMAT_A2R10G10B10_SINT_PACK32                       = 63,
-    VK_FORMAT_A2B10G10R10_UNORM_PACK32                      = 64,
-    VK_FORMAT_A2B10G10R10_SNORM_PACK32                      = 65,
-    VK_FORMAT_A2B10G10R10_USCALED_PACK32                    = 66,
-    VK_FORMAT_A2B10G10R10_SSCALED_PACK32                    = 67,
-    VK_FORMAT_A2B10G10R10_UINT_PACK32                       = 68,
-    VK_FORMAT_A2B10G10R10_SINT_PACK32                       = 69,
-    VK_FORMAT_R16_UNORM                                     = 70,
-    VK_FORMAT_R16_SNORM                                     = 71,
-    VK_FORMAT_R16_USCALED                                   = 72,
-    VK_FORMAT_R16_SSCALED                                   = 73,
-    VK_FORMAT_R16_UINT                                      = 74,
-    VK_FORMAT_R16_SINT                                      = 75,
-    VK_FORMAT_R16_SFLOAT                                    = 76,
-    VK_FORMAT_R16G16_UNORM                                  = 77,
-    VK_FORMAT_R16G16_SNORM                                  = 78,
-    VK_FORMAT_R16G16_USCALED                                = 79,
-    VK_FORMAT_R16G16_SSCALED                                = 80,
-    VK_FORMAT_R16G16_UINT                                   = 81,
-    VK_FORMAT_R16G16_SINT                                   = 82,
-    VK_FORMAT_R16G16_SFLOAT                                 = 83,
-    VK_FORMAT_R16G16B16_UNORM                               = 84,
-    VK_FORMAT_R16G16B16_SNORM                               = 85,
-    VK_FORMAT_R16G16B16_USCALED                             = 86,
-    VK_FORMAT_R16G16B16_SSCALED                             = 87,
-    VK_FORMAT_R16G16B16_UINT                                = 88,
-    VK_FORMAT_R16G16B16_SINT                                = 89,
-    VK_FORMAT_R16G16B16_SFLOAT                              = 90,
-    VK_FORMAT_R16G16B16A16_UNORM                            = 91,
-    VK_FORMAT_R16G16B16A16_SNORM                            = 92,
-    VK_FORMAT_R16G16B16A16_USCALED                          = 93,
-    VK_FORMAT_R16G16B16A16_SSCALED                          = 94,
-    VK_FORMAT_R16G16B16A16_UINT                             = 95,
-    VK_FORMAT_R16G16B16A16_SINT                             = 96,
-    VK_FORMAT_R16G16B16A16_SFLOAT                           = 97,
-    VK_FORMAT_R32_UINT                                      = 98,
-    VK_FORMAT_R32_SINT                                      = 99,
-    VK_FORMAT_R32_SFLOAT                                    = 100,
-    VK_FORMAT_R32G32_UINT                                   = 101,
-    VK_FORMAT_R32G32_SINT                                   = 102,
-    VK_FORMAT_R32G32_SFLOAT                                 = 103,
-    VK_FORMAT_R32G32B32_UINT                                = 104,
-    VK_FORMAT_R32G32B32_SINT                                = 105,
-    VK_FORMAT_R32G32B32_SFLOAT                              = 106,
-    VK_FORMAT_R32G32B32A32_UINT                             = 107,
-    VK_FORMAT_R32G32B32A32_SINT                             = 108,
-    VK_FORMAT_R32G32B32A32_SFLOAT                           = 109,
-    VK_FORMAT_R64_UINT                                      = 110,
-    VK_FORMAT_R64_SINT                                      = 111,
-    VK_FORMAT_R64_SFLOAT                                    = 112,
-    VK_FORMAT_R64G64_UINT                                   = 113,
-    VK_FORMAT_R64G64_SINT                                   = 114,
-    VK_FORMAT_R64G64_SFLOAT                                 = 115,
-    VK_FORMAT_R64G64B64_UINT                                = 116,
-    VK_FORMAT_R64G64B64_SINT                                = 117,
-    VK_FORMAT_R64G64B64_SFLOAT                              = 118,
-    VK_FORMAT_R64G64B64A64_UINT                             = 119,
-    VK_FORMAT_R64G64B64A64_SINT                             = 120,
-    VK_FORMAT_R64G64B64A64_SFLOAT                           = 121,
-    VK_FORMAT_B10G11R11_UFLOAT_PACK32                       = 122,
-    VK_FORMAT_E5B9G9R9_UFLOAT_PACK32                        = 123,
-    VK_FORMAT_D16_UNORM                                     = 124,
-    VK_FORMAT_X8_D24_UNORM_PACK32                           = 125,
-    VK_FORMAT_D32_SFLOAT                                    = 126,
-    VK_FORMAT_S8_UINT                                       = 127,
-    VK_FORMAT_D16_UNORM_S8_UINT                             = 128,
-    VK_FORMAT_D24_UNORM_S8_UINT                             = 129,
-    VK_FORMAT_D32_SFLOAT_S8_UINT                            = 130,
-    VK_FORMAT_BC1_RGB_UNORM_BLOCK                           = 131,
-    VK_FORMAT_BC1_RGB_SRGB_BLOCK                            = 132,
-    VK_FORMAT_BC1_RGBA_UNORM_BLOCK                          = 133,
-    VK_FORMAT_BC1_RGBA_SRGB_BLOCK                           = 134,
-    VK_FORMAT_BC2_UNORM_BLOCK                               = 135,
-    VK_FORMAT_BC2_SRGB_BLOCK                                = 136,
-    VK_FORMAT_BC3_UNORM_BLOCK                               = 137,
-    VK_FORMAT_BC3_SRGB_BLOCK                                = 138,
-    VK_FORMAT_BC4_UNORM_BLOCK                               = 139,
-    VK_FORMAT_BC4_SNORM_BLOCK                               = 140,
-    VK_FORMAT_BC5_UNORM_BLOCK                               = 141,
-    VK_FORMAT_BC5_SNORM_BLOCK                               = 142,
-    VK_FORMAT_BC6H_UFLOAT_BLOCK                             = 143,
-    VK_FORMAT_BC6H_SFLOAT_BLOCK                             = 144,
-    VK_FORMAT_BC7_UNORM_BLOCK                               = 145,
-    VK_FORMAT_BC7_SRGB_BLOCK                                = 146,
-    VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK                       = 147,
-    VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK                        = 148,
-    VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK                     = 149,
-    VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK                      = 150,
-    VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK                     = 151,
-    VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK                      = 152,
-    VK_FORMAT_EAC_R11_UNORM_BLOCK                           = 153,
-    VK_FORMAT_EAC_R11_SNORM_BLOCK                           = 154,
-    VK_FORMAT_EAC_R11G11_UNORM_BLOCK                        = 155,
-    VK_FORMAT_EAC_R11G11_SNORM_BLOCK                        = 156,
-    VK_FORMAT_ASTC_4x4_UNORM_BLOCK                          = 157,
-    VK_FORMAT_ASTC_4x4_SRGB_BLOCK                           = 158,
-    VK_FORMAT_ASTC_5x4_UNORM_BLOCK                          = 159,
-    VK_FORMAT_ASTC_5x4_SRGB_BLOCK                           = 160,
-    VK_FORMAT_ASTC_5x5_UNORM_BLOCK                          = 161,
-    VK_FORMAT_ASTC_5x5_SRGB_BLOCK                           = 162,
-    VK_FORMAT_ASTC_6x5_UNORM_BLOCK                          = 163,
-    VK_FORMAT_ASTC_6x5_SRGB_BLOCK                           = 164,
-    VK_FORMAT_ASTC_6x6_UNORM_BLOCK                          = 165,
-    VK_FORMAT_ASTC_6x6_SRGB_BLOCK                           = 166,
-    VK_FORMAT_ASTC_8x5_UNORM_BLOCK                          = 167,
-    VK_FORMAT_ASTC_8x5_SRGB_BLOCK                           = 168,
-    VK_FORMAT_ASTC_8x6_UNORM_BLOCK                          = 169,
-    VK_FORMAT_ASTC_8x6_SRGB_BLOCK                           = 170,
-    VK_FORMAT_ASTC_8x8_UNORM_BLOCK                          = 171,
-    VK_FORMAT_ASTC_8x8_SRGB_BLOCK                           = 172,
-    VK_FORMAT_ASTC_10x5_UNORM_BLOCK                         = 173,
-    VK_FORMAT_ASTC_10x5_SRGB_BLOCK                          = 174,
-    VK_FORMAT_ASTC_10x6_UNORM_BLOCK                         = 175,
-    VK_FORMAT_ASTC_10x6_SRGB_BLOCK                          = 176,
-    VK_FORMAT_ASTC_10x8_UNORM_BLOCK                         = 177,
-    VK_FORMAT_ASTC_10x8_SRGB_BLOCK                          = 178,
-    VK_FORMAT_ASTC_10x10_UNORM_BLOCK                        = 179,
-    VK_FORMAT_ASTC_10x10_SRGB_BLOCK                         = 180,
-    VK_FORMAT_ASTC_12x10_UNORM_BLOCK                        = 181,
-    VK_FORMAT_ASTC_12x10_SRGB_BLOCK                         = 182,
-    VK_FORMAT_ASTC_12x12_UNORM_BLOCK                        = 183,
-    VK_FORMAT_ASTC_12x12_SRGB_BLOCK                         = 184,
-
-    //@vulkan1_1
-    VK_FORMAT_G8B8G8R8_422_UNORM                            = 1000156000,
-    VK_FORMAT_B8G8R8G8_422_UNORM                            = 1000156001,
-    VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM                     = 1000156002,
-    VK_FORMAT_G8_B8R8_2PLANE_420_UNORM                      = 1000156003,
-    VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM                     = 1000156004,
-    VK_FORMAT_G8_B8R8_2PLANE_422_UNORM                      = 1000156005,
-    VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM                     = 1000156006,
-    VK_FORMAT_R10X6_UNORM_PACK16                            = 1000156007,
-    VK_FORMAT_R10X6G10X6_UNORM_2PACK16                      = 1000156008,
-    VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16            = 1000156009,
-    VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16        = 1000156010,
-    VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16        = 1000156011,
-    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16    = 1000156012,
-    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16     = 1000156013,
-    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16    = 1000156014,
-    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16     = 1000156015,
-    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16    = 1000156016,
-    VK_FORMAT_R12X4_UNORM_PACK16                            = 1000156017,
-    VK_FORMAT_R12X4G12X4_UNORM_2PACK16                      = 1000156018,
-    VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16            = 1000156019,
-    VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16        = 1000156020,
-    VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16        = 1000156021,
-    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16    = 1000156022,
-    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16     = 1000156023,
-    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16    = 1000156024,
-    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16     = 1000156025,
-    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16    = 1000156026,
-    VK_FORMAT_G16B16G16R16_422_UNORM                        = 1000156027,
-    VK_FORMAT_B16G16R16G16_422_UNORM                        = 1000156028,
-    VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM                  = 1000156029,
-    VK_FORMAT_G16_B16R16_2PLANE_420_UNORM                   = 1000156030,
-    VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM                  = 1000156031,
-    VK_FORMAT_G16_B16R16_2PLANE_422_UNORM                   = 1000156032,
-    VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM                  = 1000156033,
-
-    //@extension("VK_IMG_format_pvrtc") // 28
-    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG                   = 1000054000,
-    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG                   = 1000054001,
-    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG                   = 1000054002,
-    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG                   = 1000054003,
-    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG                    = 1000054004,
-    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG                    = 1000054005,
-    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG                    = 1000054006,
-    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG                    = 1000054007,
-
-    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-    VK_FORMAT_G8B8G8R8_422_UNORM_KHR                            = 1000156000,
-    VK_FORMAT_B8G8R8G8_422_UNORM_KHR                            = 1000156001,
-    VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR                     = 1000156002,
-    VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR                      = 1000156003,
-    VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR                     = 1000156004,
-    VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR                      = 1000156005,
-    VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR                     = 1000156006,
-    VK_FORMAT_R10X6_UNORM_PACK16_KHR                            = 1000156007,
-    VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR                      = 1000156008,
-    VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR            = 1000156009,
-    VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR        = 1000156010,
-    VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR        = 1000156011,
-    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR    = 1000156012,
-    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR     = 1000156013,
-    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR    = 1000156014,
-    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR     = 1000156015,
-    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR    = 1000156016,
-    VK_FORMAT_R12X4_UNORM_PACK16_KHR                            = 1000156017,
-    VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR                      = 1000156018,
-    VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR            = 1000156019,
-    VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR        = 1000156020,
-    VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR        = 1000156021,
-    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR    = 1000156022,
-    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR     = 1000156023,
-    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR    = 1000156024,
-    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR     = 1000156025,
-    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR    = 1000156026,
-    VK_FORMAT_G16B16G16R16_422_UNORM_KHR                        = 1000156027,
-    VK_FORMAT_B16G16R16G16_422_UNORM_KHR                        = 1000156028,
-    VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR                  = 1000156029,
-    VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR                   = 1000156030,
-    VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR                  = 1000156031,
-    VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR                   = 1000156032,
-    VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR                  = 1000156033,
-}
-
-/// Structure type enumerant
-enum VkStructureType {
-    VK_STRUCTURE_TYPE_APPLICATION_INFO                          = 0,
-    VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO                      = 1,
-    VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO                  = 2,
-    VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO                        = 3,
-    VK_STRUCTURE_TYPE_SUBMIT_INFO                               = 4,
-    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO                      = 5,
-    VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE                       = 6,
-    VK_STRUCTURE_TYPE_BIND_SPARSE_INFO                          = 7,
-    VK_STRUCTURE_TYPE_FENCE_CREATE_INFO                         = 8,
-    VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO                     = 9,
-    VK_STRUCTURE_TYPE_EVENT_CREATE_INFO                         = 10,
-    VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO                    = 11,
-    VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO                        = 12,
-    VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO                   = 13,
-    VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO                         = 14,
-    VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO                    = 15,
-    VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO                 = 16,
-    VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO                = 17,
-    VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO         = 18,
-    VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO   = 19,
-    VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20,
-    VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO   = 21,
-    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO       = 22,
-    VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO  = 23,
-    VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO    = 24,
-    VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO  = 25,
-    VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO    = 26,
-    VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO        = 27,
-    VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO             = 28,
-    VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO              = 29,
-    VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO               = 30,
-    VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO                       = 31,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO         = 32,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO               = 33,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO              = 34,
-    VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET                      = 35,
-    VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET                       = 36,
-    VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO                   = 37,
-    VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO                   = 38,
-    VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO                  = 39,
-    VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO              = 40,
-    VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO           = 41,
-    VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO                 = 42,
-    VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO                    = 43,
-    VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER                     = 44,
-    VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER                      = 45,
-    VK_STRUCTURE_TYPE_MEMORY_BARRIER                            = 46,
-    VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO               = 47,
-    VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO                 = 48,
-
-    //@vulkan1_1
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES       = 1000094000,
-    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO                   = 1000157000,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO                    = 1000157001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES    = 1000083000,
-    VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS             = 1000127000,
-    VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO            = 1000127001,
-    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO                = 1000060000,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO       = 1000060003,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO    = 1000060004,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO                  = 1000060005,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO             = 1000060006,
-    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO      = 1000060013,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO       = 1000060014,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES          = 1000070000,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO           = 1000070001,
-    VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2         = 1000146000,
-    VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2          = 1000146001,
-    VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2   = 1000146002,
-    VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2                     = 1000146003,
-    VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2        = 1000146004,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2                = 1000059000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2              = 1000059001,
-    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2                       = 1000059002,
-    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2                 = 1000059003,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2       = 1000059004,
-    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2                 = 1000059005,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2       = 1000059006,
-    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2          = 1000059007,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2            = 1000059008,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES             = 1000117000,
-    VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO       = 1000117001,
-    VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO                          = 1000117002,
-    VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO = 1000117003,
-    VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO                 = 1000053000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES                = 1000053001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES              = 1000053002,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES         = 1000120000,
-    VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO                             = 1000145000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES         = 1000145001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES       = 1000145002,
-    VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2                               = 1000145003,
-    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO              = 1000156000,
-    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO                     = 1000156001,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO                      = 1000156002,
-    VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO              = 1000156003,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES = 1000156004,
-    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES  = 1000156005,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO            = 1000085000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO        = 1000071000,
-    VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES                  = 1000071001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO              = 1000071002,
-    VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES                        = 1000071003,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES                     = 1000071004,
-    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO                = 1000072000,
-    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO                 = 1000072001,
-    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO                       = 1000072002,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO               = 1000112000,
-    VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES                         = 1000112001,
-    VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO                          = 1000113000,
-    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO                      = 1000077000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO           = 1000076000,
-    VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES                     = 1000076001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES          = 1000168000,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT                     = 1000168001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES    = 1000063000,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR             = 1000060007,
-
-    //@extension("VK_KHR_swapchain") // 2
-    VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR                 = 1000001000,
-    VK_STRUCTURE_TYPE_PRESENT_INFO_KHR                          = 1000001001,
-    // added as interaction from VK_KHR_device_group / VK 1.1
-    VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR           = 1000060008,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR      = 1000060009,
-    VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHR               = 1000060010,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHR             = 1000060011,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHR    = 1000060012,
-
-    //@extension("VK_KHR_display") // 3
-    VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR              = 1000002000,
-    VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR           = 1000002001,
-
-    //@extension("VK_KHR_display_swapchain") // 4
-    VK_STRUCTURE_TYPE_DISPLAY_DISPLAY_PRESENT_INFO_KHR          = 1000003000,
-
-    //@extension("VK_KHR_xlib_surface") // 5
-    VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR              = 1000004000,
-
-    //@extension("VK_KHR_xcb_surface") // 6
-    VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR               = 1000005000,
-
-    //@extension("VK_KHR_wayland_surface") // 7
-    VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR           = 1000006000,
-
-    //@extension("VK_KHR_android_surface") // 9
-    VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR           = 1000008000,
-
-    //@extension("VK_KHR_win32_surface") // 10
-    VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR             = 1000009000,
-
-    //@extension("VK_ANDROID_native_buffer") // 11
-    VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID                     = 1000010000,
-    VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID       = 1000010001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID = 1000010002,
-
-    //@extension("VK_EXT_debug_report") // 12
-    VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT     = 1000011000,
-
-    //@extension("VK_AMD_rasterization_order") // 19
-    VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000,
-
-    //@extension("VK_EXT_debug_marker") // 23
-    VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT         = 1000022000,
-    VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT          = 1000022001,
-    VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT              = 1000022002,
-
-    //@extension("VK_NV_dedicated_allocation") // 27
-    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000,
-    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001,
-    VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002,
-
-    //@extension("VK_EXT_transform_feedback") // 29
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT       = 1000028000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT     = 1000028001,
-    VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT   = 1000028002,
-
-    //@extension("VK_AMD_texture_gather_bias_lod") // 42
-    VK_STRUCTURE_TYPE_TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD  = 1000041000,
-
-    //@extension("VK_NV_corner_sampled_image") // 51
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV  = 1000050000,
-
-    //@extension("VK_KHR_multiview") // 54
-    VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR     = 1000053000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR    = 1000053001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR  = 1000053002,
-
-    //@extension("VK_NV_external_memory") // 57
-    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV      = 1000056000,
-    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV            = 1000056001,
-
-    //@extension("VK_NV_external_memory_win32") // 58
-    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV        = 1000057000,
-    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV        = 1000057001,
-
-    //@extension("VK_NV_win32_keyed_mutex") // 59
-    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000,
-
-    //@extension("VK_KHR_get_physical_device_properties2") // 60
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR            = 1000059000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR          = 1000059001,
-    VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR                   = 1000059002,
-    VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR             = 1000059003,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR   = 1000059004,
-    VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR             = 1000059005,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR   = 1000059006,
-    VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR      = 1000059007,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
-
-    //@extension("VK_KHR_device_group") // 61
-    VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR            = 1000060000,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHR   = 1000060003,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHR = 1000060004,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHR              = 1000060005,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHR         = 1000060006,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR     = 1000060007,
-    // tokens 08-12 are listed with VK_KHR_swapchain
-    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHR  = 1000060013,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHR   = 1000060014,
-
-    //@extension("VK_EXT_validation_flags") // 62
-    VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT                      = 1000061000,
-
-    //@extension("VK_NN_vi_surface") // 63
-    VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN                 = 1000062000,
-
-    //@extension("VK_EXT_astc_decode_mode") // 68
-    VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT           = 1000067000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT  = 1000067001,
-
-    //@extension("VK_KHR_device_group_creation") // 71
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR      = 1000070000,
-    VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR       = 1000070001,
-
-    //@extension("VK_KHR_external_memory_capabilities") // 72
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR    = 1000071000,
-    VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR      = 1000071001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR  = 1000071002,
-    VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR            = 1000071003,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR         = 1000071004,
-
-    //@extension("VK_KHR_external_memory") // 73
-    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR    = 1000072000,
-    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR     = 1000072001,
-    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR           = 1000072002,
-
-    //@extension("VK_KHR_external_memory_win32") // 74
-    VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR       = 1000073000,
-    VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR       = 1000073001,
-    VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR        = 1000073002,
-
-    //@extension("VK_KHR_external_memory_fd") // 75
-    VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR                 = 1000074000,
-    VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR                  = 1000074001,
-
-    //@extension("VK_KHR_win32_keyed_mutex") // 76
-    VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR    = 1000075000,
-
-    //@extension("VK_KHR_external_semaphore_capabilities") // 77
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR   = 1000076000,
-    VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR         = 1000076001,
-
-    //@extension("VK_KHR_external_semaphore") // 78
-    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR          = 1000077000,
-
-    //@extension("VK_KHR_external_semaphore_win32") // 79
-    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR    = 1000078000,
-    VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR    = 1000078001,
-    VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHR               = 1000078002,
-
-    //@extension("VK_KHR_external_semaphore_fd") // 80
-    VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR              = 1000079000,
-    VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR                 = 1000079001,
-
-    //@extension("VK_KHR_push_descriptor") // 81
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR    = 1000080000,
-
-    //@extension("VK_KHR_16bit_storage") // 84
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR    = 1000083000,
-
-    //@extension("VK_KHR_incremental_present") // 85
-    VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR                       = 1000084000,
-
-    //@extension("VK_EXT_conditional_rendering") // 82
-    VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT = 1000081000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT        = 1000081001,
-    VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT                      = 1000081002,
-
-    //@extension("VK_KHR_shader_float16_int8") // 83
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR     = 1000082000,
-
-    //@extension("VK_KHR_descriptor_update_template") // 86
-    VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR    = 1000085000,
-
-    //@extension("VK_NVX_device_generated_commands") // 87
-    VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX              = 1000086000,
-    VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX  = 1000086001,
-    VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX             = 1000086002,
-    VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX   = 1000086003,
-    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX      = 1000086004,
-    VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX    = 1000086005,
-
-    //@extension("VK_NV_clip_space_w_scaling") // 88
-    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV  = 1000087000,
-
-    //@extension("VK_EXT_display_surface_counter") // 91
-    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT                = 1000090000,
-
-    //@extension("VK_EXT_display_control") // 92
-    VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT                    = 1000091000,
-    VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT                     = 1000091001,
-    VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT                    = 1000091002,
-    VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT         = 1000091003,
-
-    //@extension("VK_GOOGLE_display_timing") // 93
-    VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE                 = 1000092000,
-
-    //@extension("VK_NVX_multiview_per_view_attributes") // 98
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX  = 1000097000,
-
-    //@extension("VK_NV_viewport_swizzle") // 99
-    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV    = 1000098000,
-
-    //@extension("VK_EXT_discard_rectangles") // 100
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT  = 1000099000,
-    VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT  = 1000099001,
-
-    //@extension("VK_EXT_conservative_rasterization") // 102
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT = 1000101000,
-    VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT = 1000101001,
-
-    //@extension("VK_KHR_create_renderpass2") // 110
-    VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR              = 1000109000,
-    VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR                = 1000109001,
-    VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR                 = 1000109002,
-    VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR                  = 1000109003,
-    VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR             = 1000109004,
-    VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO_KHR                    = 1000109005,
-    VK_STRUCTURE_TYPE_SUBPASS_END_INFO_KHR                      = 1000109006,
-
-    //@extension("VK_EXT_hdr_metadata") // 106
-    VK_STRUCTURE_TYPE_HDR_METADATA_EXT                          = 1000105000,
-
-    //@extension("VK_KHR_shared_presentable_image") // 112
-    VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR   = 1000111000,
-
-    //@extension("VK_KHR_external_fence_capabilities") // 113
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR   = 1000112000,
-    VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES_KHR             = 1000112001,
-
-    //@extension("VK_KHR_external_fence") // 114
-    VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO_KHR              = 1000113000,
-
-    //@extension("VK_KHR_external_fence_win32") // 115
-    VK_STRUCTURE_TYPE_IMPORT_FENCE_WIN32_HANDLE_INFO_KHR        = 1000114000,
-    VK_STRUCTURE_TYPE_EXPORT_FENCE_WIN32_HANDLE_INFO_KHR        = 1000114001,
-    VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR           = 1000114002,
-
-    //@extension("VK_KHR_external_fence_fd") // 117
-    VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR                  = 1000115000,
-    VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR                     = 1000115001,
-
-    //@extension("VK_KHR_maintenance2") // 118
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR             = 1000117000,
-    VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR       = 1000117001,
-    VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR                          = 1000117002,
-    VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003,
-
-    //@extension("VK_KHR_get_surface_capabilities2") // 120
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR        = 1000119000,
-    VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR                = 1000119001,
-    VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR                      = 1000119002,
-
-    //@extension("VK_KHR_variable_pointers") // 121
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = 1000120000,
-
-    //@extension("VK_KHR_display_properties2") // 122
-    VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR                  = 1000121000,
-    VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR            = 1000121001,
-    VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR             = 1000121002,
-    VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR                  = 1000121003,
-    VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR          = 1000121004,
-
-    //@extension("VK_MVK_ios_surface") // 123
-    VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK               = 1000122000,
-
-    //@extension("VK_MVK_macos_surface") // 124
-    VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK             = 1000123000,
-
-    //@extension("VK_KHR_dedicated_allocation") // 128
-    VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR         = 1000127000,
-    VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR        = 1000127001,
-
-    //@extension("VK_EXT_debug_utils") // 129
-    VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT          = 1000128000,
-    VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT           = 1000128001,
-    VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT                     = 1000128002,
-    VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT   = 1000128003,
-    VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT     = 1000128004,
-
-    //@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-    VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID             = 1000129000,
-    VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID        = 1000129001,
-    VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID = 1000129002,
-    VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID       = 1000129003,
-    VK_STRUCTURE_TYPE_MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID   = 1000129004,
-    VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID                           = 1000129005,
-
-    //@extension("VK_EXT_sampler_filter_minmax") // 131
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000,
-    VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001,
-
-    //@extension("VK_EXT_inline_uniform_block") // 139
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT     = 1000138000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT   = 1000138001,
-    VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT         = 1000138002,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT  = 1000138003,
-
-    //@extension("VK_EXT_sample_locations") // 144
-    VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT                         = 1000143000,
-    VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT       = 1000143001,
-    VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT   = 1000143002,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT   = 1000143003,
-    VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT                        = 1000143004,
-
-    //@extension("VK_KHR_get_memory_requirements2") // 147
-    VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR         = 1000146000,
-    VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR          = 1000146001,
-    VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR   = 1000146002,
-    VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR                     = 1000146003,
-    VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR        = 1000146004,
-
-    //@extension("VK_KHR_image_format_list") // 148
-    VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR             = 1000147000,
-
-    //@extension("VK_EXT_blend_operation_advanced") // 149
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001,
-    VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002,
-
-    //@extension("VK_NV_fragment_coverage_to_color") // 150
-    VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000,
-
-    //@extension("VK_NV_framebuffer_mixed_samples") // 153
-    VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000,
-
-    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR              = 1000156000,
-    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR                     = 1000156001,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR                      = 1000156002,
-    VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR              = 1000156003,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004,
-    VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR  = 1000156005,
-
-    //@extension("VK_EXT_image_drm_format_modifier") // 159
-    VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT               = 1000158000,
-    VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT                    = 1000158001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT    = 1000158002,
-    VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT        = 1000158003,
-    VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT    = 1000158004,
-    VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT              = 1000158005,
-
-    //@extension("VK_KHR_bind_memory2") // 158
-    VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR                       = 1000157000,
-    VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR                        = 1000157001,
-
-    //@extension("VK_EXT_validation_cache") // 161
-    VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT                  = 1000160000,
-    VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT    = 1000160001,
-
-    //@extension("VK_EXT_descriptor_indexing") // 162
-    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT           = 1000161000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT              = 1000161001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT            = 1000161002,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT    = 1000161003,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT   = 1000161004,
-
-    //@extension("VK_NV_shading_rate_image") // 165
-    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV = 1000164000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV = 1000164001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV = 1000164002,
-    VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_COARSE_SAMPLE_ORDER_STATE_CREATE_INFO_NV = 1000164005,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV                   = 1000165000,
-    VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV                 = 1000165001,
-    VK_STRUCTURE_TYPE_GEOMETRY_NV                                           = 1000165003,
-    VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV                                 = 1000165004,
-    VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV                                      = 1000165005,
-    VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV            = 1000165006,
-    VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV        = 1000165007,
-    VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV    = 1000165008,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV             = 1000165009,
-    VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV               = 1000165011,
-    VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV                        = 1000165012,
-
-    //@extension("VK_NV_representative_fragment_test") // 167
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV = 1000166000,
-    VK_STRUCTURE_TYPE_PIPELINE_REPRESENTATIVE_FRAGMENT_TEST_STATE_CREATE_INFO_NV = 1000166001,
-
-    //@extension("VK_KHR_maintenance3") // 169
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR      = 1000168000,
-    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR                 = 1000168001,
-
-    //@extension("VK_EXT_global_priority") // 175
-    VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT      = 1000174000,
-
-    //@extension("VK_KHR_8bit_storage") // 178
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR         = 1000177000,
-
-    //@extension("VK_EXT_external_memory_host") // 179
-    VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT                   = 1000178000,
-    VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT                    = 1000178001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT   = 1000178002,
-
-    //@extension("VK_KHR_shader_atomic_int64") // 181
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR  = 1000180000,
-
-    //@extension("VK_EXT_calibrated_timestamps") // 185
-    VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT                     = 1000184000,
-
-    //@extension("VK_KHR_driver_properties") // 197
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR             = 1000196000,
-
-    //@extension("VK_KHR_shader_float_controls") // 198
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR     = 1000197000,
-
-    //@extension("VK_AMD_shader_core_properties") // 186
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD        = 1000185000,
-
-    //@extension("VK_AMD_memory_overallocation_behavior") // 190
-    VK_STRUCTURE_TYPE_DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD      = 1000189000,
-
-    //@extension("VK_EXT_vertex_attribute_divisor") // 191
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT   = 1000190000,
-    VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT       = 1000190001,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT     = 1000190002,
-
-    //@extension("VK_NV_device_diagnostic_checkpoints") // 207
-    VK_STRUCTURE_TYPE_CHECKPOINT_DATA_NV                        = 1000206000,
-    VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_NV     = 1000206001,
-
-    //@extension("VK_KHR_vulkan_memory_model") // 212
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = 1000211000,
-
-    //@extension("VK_EXT_pci_bus_info") // 213
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT   = 1000212000,
-
-    //@extension("VK_FUCHSIA_imagepipe_surface") // 215
-    VK_STRUCTURE_TYPE_IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA     = 1000214000,
-
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT     = 1000218000,
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT   = 1000218001,
-    VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT      = 1000218002,
-
-    //@extension("VK_EXT_scalar_block_layout")
-    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT  = 1000221000,
-
-    //@extension("VK_EXT_separate_stencil_usage") // 247
-    VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO_EXT       = 1000246000,
-}
-
-enum VkSubpassContents {
-    VK_SUBPASS_CONTENTS_INLINE                              = 0x00000000,
-    VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS           = 0x00000001,
-}
-
-enum VkPipelineCacheHeaderVersion {
-    VK_PIPELINE_CACHE_HEADER_VERSION_ONE                    = 1,
-}
-
-@lastUnused(-11)
-/// Error and return codes
-enum VkResult {
-    // Return codes for successful operation execution (positive values)
-    VK_SUCCESS                                              = 0,
-    VK_NOT_READY                                            = 1,
-    VK_TIMEOUT                                              = 2,
-    VK_EVENT_SET                                            = 3,
-    VK_EVENT_RESET                                          = 4,
-    VK_INCOMPLETE                                           = 5,
-
-    //@extension("VK_KHR_swapchain") // 2
-    VK_SUBOPTIMAL_KHR                                       = 1000001003,
-
-    // Error codes (negative values)
-    VK_ERROR_OUT_OF_HOST_MEMORY                             = 0xFFFFFFFF, // -1
-    VK_ERROR_OUT_OF_DEVICE_MEMORY                           = 0xFFFFFFFE, // -2
-    VK_ERROR_INITIALIZATION_FAILED                          = 0xFFFFFFFD, // -3
-    VK_ERROR_DEVICE_LOST                                    = 0xFFFFFFFC, // -4
-    VK_ERROR_MEMORY_MAP_FAILED                              = 0xFFFFFFFB, // -5
-    VK_ERROR_LAYER_NOT_PRESENT                              = 0xFFFFFFFA, // -6
-    VK_ERROR_EXTENSION_NOT_PRESENT                          = 0xFFFFFFF9, // -7
-    VK_ERROR_FEATURE_NOT_PRESENT                            = 0xFFFFFFF8, // -8
-    VK_ERROR_INCOMPATIBLE_DRIVER                            = 0xFFFFFFF7, // -9
-    VK_ERROR_TOO_MANY_OBJECTS                               = 0xFFFFFFF6, // -10
-    VK_ERROR_FORMAT_NOT_SUPPORTED                           = 0xFFFFFFF5, // -11
-    VK_ERROR_FRAGMENTED_POOL                                = 0xFFFFFFF4, // -12
-
-    //@vulkan1_1
-    VK_ERROR_OUT_OF_POOL_MEMORY                             = 0xC4642878, // -1000069000
-    VK_ERROR_INVALID_EXTERNAL_HANDLE                        = 0xC4641CBD, // -1000072003
-
-    //@extension("VK_KHR_surface") // 1
-    VK_ERROR_SURFACE_LOST_KHR                               = 0xC4653600, // -1000000000
-    VK_ERROR_NATIVE_WINDOW_IN_USE_KHR                       = 0xC46535FF, // -1000000001
-
-    //@extension("VK_KHR_swapchain") // 2
-    VK_ERROR_OUT_OF_DATE_KHR                                = 0xC4653214, // -1000001004
-
-    //@extension("VK_KHR_display_swapchain") // 4
-    VK_ERROR_INCOMPATIBLE_DISPLAY_KHR                       = 0xC4652A47, // -1000003001
-
-    //@extension("VK_EXT_debug_report") // 12
-    VK_ERROR_VALIDATION_FAILED_EXT                          = 0xC4650B07, // -1000011001
-
-    //@extension("VK_NV_glsl_shader") // 13
-    VK_ERROR_INVALID_SHADER_NV                              = 0xC4650720, // -1000012000
-
-    //@extension("VK_KHR_maintenance1") // 70
-    VK_ERROR_OUT_OF_POOL_MEMORY_KHR                         = 0xC4642878, // -1000069000
-
-    //@extension("VK_KHR_external_memory") // 73
-    VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR                    = 0xC4641CBD, // -1000072003
-
-    //@extension("VK_EXT_image_drm_format_modifier") // 159
-    VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT   = 0xC462CCD0, // -1000158000
-
-    //@extension("VK_EXT_descriptor_indexing") // 162
-    VK_ERROR_FRAGMENTATION_EXT                              = 0xc462c118, // -1000161000
-
-    //@extension("VK_EXT_global_priority") // 175
-    VK_ERROR_NOT_PERMITTED_EXT                              = 0xC4628E4F, // -1000174001
-}
-
-enum VkDynamicState {
-    VK_DYNAMIC_STATE_VIEWPORT                               = 0x00000000,
-    VK_DYNAMIC_STATE_SCISSOR                                = 0x00000001,
-    VK_DYNAMIC_STATE_LINE_WIDTH                             = 0x00000002,
-    VK_DYNAMIC_STATE_DEPTH_BIAS                             = 0x00000003,
-    VK_DYNAMIC_STATE_BLEND_CONSTANTS                        = 0x00000004,
-    VK_DYNAMIC_STATE_DEPTH_BOUNDS                           = 0x00000005,
-    VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK                   = 0x00000006,
-    VK_DYNAMIC_STATE_STENCIL_WRITE_MASK                     = 0x00000007,
-    VK_DYNAMIC_STATE_STENCIL_REFERENCE                      = 0x00000008,
-
-    //@extension("VK_NV_clip_space_w_scaling") // 88
-    VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV                  = 1000087000,
-
-    //@extension("VK_EXT_discard_rectangles") // 100
-    VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT                  = 1000099000,
-
-    //@extension("VK_EXT_sample_locations") // 144
-    VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT                   = 1000143000,
-
-    //@extension("VK_NV_shading_rate_image") // 165
-    VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV       = 1000164004,
-    VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV        = 1000164006,
-
-    //@extension("VK_NV_scissor_exclusive") // 206
-    VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV                   = 1000205001,
-}
-
-enum VkObjectType {
-    VK_OBJECT_TYPE_UNKNOWN                                  = 0,
-    VK_OBJECT_TYPE_INSTANCE                                 = 1,
-    VK_OBJECT_TYPE_PHYSICAL_DEVICE                          = 2,
-    VK_OBJECT_TYPE_DEVICE                                   = 3,
-    VK_OBJECT_TYPE_QUEUE                                    = 4,
-    VK_OBJECT_TYPE_SEMAPHORE                                = 5,
-    VK_OBJECT_TYPE_COMMAND_BUFFER                           = 6,
-    VK_OBJECT_TYPE_FENCE                                    = 7,
-    VK_OBJECT_TYPE_DEVICE_MEMORY                            = 8,
-    VK_OBJECT_TYPE_BUFFER                                   = 9,
-    VK_OBJECT_TYPE_IMAGE                                    = 10,
-    VK_OBJECT_TYPE_EVENT                                    = 11,
-    VK_OBJECT_TYPE_QUERY_POOL                               = 12,
-    VK_OBJECT_TYPE_BUFFER_VIEW                              = 13,
-    VK_OBJECT_TYPE_IMAGE_VIEW                               = 14,
-    VK_OBJECT_TYPE_SHADER_MODULE                            = 15,
-    VK_OBJECT_TYPE_PIPELINE_CACHE                           = 16,
-    VK_OBJECT_TYPE_PIPELINE_LAYOUT                          = 17,
-    VK_OBJECT_TYPE_RENDER_PASS                              = 18,
-    VK_OBJECT_TYPE_PIPELINE                                 = 19,
-    VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT                    = 20,
-    VK_OBJECT_TYPE_SAMPLER                                  = 21,
-    VK_OBJECT_TYPE_DESCRIPTOR_POOL                          = 22,
-    VK_OBJECT_TYPE_DESCRIPTOR_SET                           = 23,
-    VK_OBJECT_TYPE_FRAMEBUFFER                              = 24,
-    VK_OBJECT_TYPE_COMMAND_POOL                             = 25,
-
-    //@vulkan1_1
-    VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION                 = 1000156000,
-    VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE               = 1000085000,
-
-    //@extension("VK_KHR_surface") // 1
-    VK_OBJECT_TYPE_SURFACE_KHR                              = 1000000000,
-
-    //@extension("VK_KHR_swapchain") // 2
-    VK_OBJECT_TYPE_SWAPCHAIN_KHR                            = 1000001000,
-
-    //@extension("VK_KHR_display") // 3
-    VK_OBJECT_TYPE_DISPLAY_KHR                              = 1000002000,
-    VK_OBJECT_TYPE_DISPLAY_MODE_KHR                         = 1000002001,
-
-    //@extension("VK_KHR_debug_report") // 12
-    VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT                = 1000011000,
-
-    //@extension("VK_KHR_descriptor_update_template") // 86
-    VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR           = 1000085000,
-
-    //@extension("VK_NVX_device_generated_commands") // 87
-    VK_OBJECT_TYPE_OBJECT_TABLE_NVX                         = 1000086000,
-    VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX             = 1000086001,
-
-    //@extension("VK_EXT_debug_utils") // 129
-    VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT                = 1000128000,
-
-    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-    VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR             = 1000156000,
-
-    //@extension("VK_EXT_validation_cache") // 161
-    VK_OBJECT_TYPE_VALIDATION_CACHE_EXT                     = 1000160000,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV                = 1000165000,
-}
-
-
-//@vulkan1_1 enums
-
-enum VkPointClippingBehavior {
-    VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES              = 0,
-    VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY        = 1,
-}
-
-enum VkTessellationDomainOrigin {
-    VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT                = 0,
-    VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT                = 1,
-}
-
-enum VkSamplerYcbcrModelConversion {
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY          = 0,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY        = 1,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709             = 2,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601             = 3,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020            = 4,
-}
-
-enum VkSamplerYcbcrRange {
-    VK_SAMPLER_YCBCR_RANGE_ITU_FULL = 0,
-    VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1,
-}
-
-enum VkChromaLocation {
-    VK_CHROMA_LOCATION_COSITED_EVEN = 0,
-    VK_CHROMA_LOCATION_MIDPOINT = 1,
-}
-
-enum VkDescriptorUpdateTemplateType {
-    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET = 0,
-    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1,
-}
-
-enum VkVendorId {
-    VK_VENDOR_ID_VIV                                        = 0x10001,
-    VK_VENDOR_ID_VSI                                        = 0x10002,
-    VK_VENDOR_ID_KAZAN                                      = 0x10003,
-}
-
-@extension("VK_KHR_surface") // 1
-enum VkPresentModeKHR {
-    VK_PRESENT_MODE_IMMEDIATE_KHR                           = 0x00000000,
-    VK_PRESENT_MODE_MAILBOX_KHR                             = 0x00000001,
-    VK_PRESENT_MODE_FIFO_KHR                                = 0x00000002,
-    VK_PRESENT_MODE_FIFO_RELAXED_KHR                        = 0x00000003,
-
-    //@extension("VK_KHR_shared_presentable_image") // 112
-    VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR               = 1000111000,
-    VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR           = 1000111001,
-}
-
-@extension("VK_KHR_surface") // 1
-enum VkColorSpaceKHR {
-    VK_COLOR_SPACE_SRGB_NONLINEAR_KHR                       = 0x00000000,
-
-    //@extension("VK_EXT_swapchain_colorspace") // 105
-    VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT                 = 1000104001,
-    VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT                 = 1000104002,
-    VK_COLOR_SPACE_DCI_P3_LINEAR_EXT                        = 1000104003,
-    VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT                     = 1000104004,
-    VK_COLOR_SPACE_BT709_LINEAR_EXT                         = 1000104005,
-    VK_COLOR_SPACE_BT709_NONLINEAR_EXT                      = 1000104006,
-    VK_COLOR_SPACE_BT2020_LINEAR_EXT                        = 1000104007,
-    VK_COLOR_SPACE_HDR10_ST2084_EXT                         = 1000104008,
-    VK_COLOR_SPACE_DOLBYVISION_EXT                          = 1000104009,
-    VK_COLOR_SPACE_HDR10_HLG_EXT                            = 1000104010,
-    VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT                      = 1000104011,
-    VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT                   = 1000104012,
-    VK_COLOR_SPACE_PASS_THROUGH_EXT                         = 1000104013,
-    VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT              = 1000104014,
-}
-
-@extension("VK_EXT_debug_report") // 12
-enum VkDebugReportObjectTypeEXT {
-    VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT                 = 0,
-    VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT                = 1,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT         = 2,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT                  = 3,
-    VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT                   = 4,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT               = 5,
-    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT          = 6,
-    VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT                   = 7,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT           = 8,
-    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT                  = 9,
-    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT                   = 10,
-    VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT                   = 11,
-    VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT              = 12,
-    VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT             = 13,
-    VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT              = 14,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT           = 15,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT          = 16,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT         = 17,
-    VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT             = 18,
-    VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT                = 19,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT   = 20,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT                 = 21,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT         = 22,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT          = 23,
-    VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT             = 24,
-    VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT            = 25,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT             = 26,
-    VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT           = 27,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT = 28,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT             = 29,
-    VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT        = 30,
-    VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT        = 31,
-    VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32,
-
-    //extension("VK_EXT_validation_cache") // 161
-    VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT    = 33,
-
-    //extension("VK_KHR_descriptor_update_template") // 86
-    VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000,
-
-    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-    VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT   = 1000165000,
-}
-
-@extension("VK_AMD_rasterization_order") // 19
-enum VkRasterizationOrderAMD {
-    VK_RASTERIZATION_ORDER_STRICT_AMD                       = 0,
-    VK_RASTERIZATION_ORDER_RELAXED_AMD                      = 1,
-}
-
-@extension("VK_AMD_shader_info") // 43
-enum VkShaderInfoTypeAMD {
-    VK_SHADER_INFO_TYPE_STATISTICS_AMD                      = 0,
-    VK_SHADER_INFO_TYPE_BINARY_AMD                          = 1,
-    VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD                     = 2,
-}
-
-@extension("VK_EXT_validation_flags") // 62
-enum VkValidationCheckEXT {
-    VK_VALIDATION_CHECK_ALL_EXT                             = 0,
-    VK_VALIDATION_CHECK_SHADERS_EXT                         = 1,
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-enum VkDescriptorUpdateTemplateTypeKHR {
-    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR   = 0,
-    VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1,
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-enum VkIndirectCommandsTokenTypeNVX {
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX            = 0,
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_DESCRIPTOR_SET_NVX      = 1,
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_NVX        = 2,
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_NVX       = 3,
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_NVX       = 4,
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_NVX        = 5,
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_NVX                = 6,
-    VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX            = 7,
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-enum VkObjectEntryTypeNVX {
-    VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX                 = 0,
-    VK_OBJECT_ENTRY_TYPE_PIPELINE_NVX                       = 1,
-    VK_OBJECT_ENTRY_TYPE_INDEX_BUFFER_NVX                   = 2,
-    VK_OBJECT_ENTRY_TYPE_VERTEX_BUFFER_NVX                  = 3,
-    VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX                  = 4,
-}
-
-@extension("VK_EXT_display_control") // 92
-enum VkDisplayPowerStateEXT {
-    VK_DISPLAY_POWER_STATE_OFF_EXT                          = 0,
-    VK_DISPLAY_POWER_STATE_SUSPEND_EXT                      = 1,
-    VK_DISPLAY_POWER_STATE_ON_EXT                           = 2,
-}
-
-@extension("VK_EXT_display_control") // 92
-enum VkDeviceEventTypeEXT {
-    VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT                = 0,
-}
-
-@extension("VK_EXT_display_control") // 92
-enum VkDisplayEventTypeEXT {
-    VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT               = 0,
-}
-
-@extension("VK_NV_viewport_swizzle") // 99
-enum VkViewportCoordinateSwizzleNV {
-    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV            = 0,
-    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV            = 1,
-    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV            = 2,
-    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV            = 3,
-    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV            = 4,
-    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV            = 5,
-    VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV            = 6,
-    VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV            = 7,
-}
-
-@extension("VK_EXT_discard_rectangles") // 100
-enum VkDiscardRectangleModeEXT {
-    VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0,
-    VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1,
-}
-
-@extension("VK_EXT_conservative_rasterization") // 102
-enum VkConservativeRasterizationModeEXT {
-    VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT         = 0,
-    VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT     = 1,
-    VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT    = 2,
-}
-
-@extension("VK_KHR_maintenance2") // 118
-enum VkPointClippingBehaviorKHR {
-    VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR          = 0,
-    VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR    = 1,
-}
-
-@extension("VK_KHR_maintenance2") // 118
-enum VkTessellationDomainOriginKHR {
-    VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR            = 0,
-    VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR            = 1,
-}
-
-@extension("VK_EXT_sampler_filter_minmax") // 131
-enum VkSamplerReductionModeEXT {
-    VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT          = 0,
-    VK_SAMPLER_REDUCTION_MODE_MIN_EXT                       = 1,
-    VK_SAMPLER_REDUCTION_MODE_MAX_EXT                       = 2,
-}
-
-@extension("VK_EXT_blend_operation_advanced") // 149
-enum VkBlendOverlapEXT {
-    VK_BLEND_OVERLAP_UNCORRELATED_EXT                       = 0,
-    VK_BLEND_OVERLAP_DISJOINT_EXT                           = 1,
-    VK_BLEND_OVERLAP_CONJOINT_EXT                           = 2,
-}
-
-@extension("VK_NV_framebuffer_mixed_samples") // 153
-enum VkCoverageModulationModeNV {
-    VK_COVERAGE_MODULATION_MODE_NONE_NV                     = 0,
-    VK_COVERAGE_MODULATION_MODE_RGB_NV                      = 1,
-    VK_COVERAGE_MODULATION_MODE_ALPHA_NV                    = 2,
-    VK_COVERAGE_MODULATION_MODE_RGBA_NV                     = 3,
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-enum VkSamplerYcbcrModelConversionKHR {
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR      = 0,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR    = 1,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR         = 2,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR         = 3,
-    VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR        = 4,
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-enum VkSamplerYcbcrRangeKHR {
-    VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR                     = 0,
-    VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR                   = 1,
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-enum VkChromaLocationKHR {
-    VK_CHROMA_LOCATION_COSITED_EVEN_KHR                     = 0,
-    VK_CHROMA_LOCATION_MIDPOINT_KHR                         = 1,
-}
-
-@extension("VK_EXT_validation_cache") // 161
-enum VkValidationCacheHeaderVersionEXT {
-    VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT              = 1,
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-enum VkShadingRatePaletteEntryNV {
-    VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV                 = 0,
-    VK_SHADING_RATE_PALETTE_ENTRY_16_INVOCATIONS_PER_PIXEL_NV       = 1,
-    VK_SHADING_RATE_PALETTE_ENTRY_8_INVOCATIONS_PER_PIXEL_NV        = 2,
-    VK_SHADING_RATE_PALETTE_ENTRY_4_INVOCATIONS_PER_PIXEL_NV        = 3,
-    VK_SHADING_RATE_PALETTE_ENTRY_2_INVOCATIONS_PER_PIXEL_NV        = 4,
-    VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV         = 5,
-    VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV    = 6,
-    VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV    = 7,
-    VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV    = 8,
-    VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV    = 9,
-    VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV    = 10,
-    VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV    = 11,
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-enum VkCoarseSampleOrderTypeNV {
-    VK_COARSE_SAMPLE_ORDER_TYPE_DEFAULT_NV                  = 0,
-    VK_COARSE_SAMPLE_ORDER_TYPE_CUSTOM_NV                   = 1,
-    VK_COARSE_SAMPLE_ORDER_TYPE_PIXEL_MAJOR_NV              = 2,
-    VK_COARSE_SAMPLE_ORDER_TYPE_SAMPLE_MAJOR_NV             = 3,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-enum VkRayTracingShaderGroupTypeNV {
-    VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV                 = 0,
-    VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV     = 1,
-    VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV    = 2,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-enum VkGeometryTypeNV {
-    VK_GEOMETRY_TYPE_TRIANGLES_NV                           = 0,
-    VK_GEOMETRY_TYPE_AABBS_NV                               = 1,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-enum VkAccelerationStructureTypeNV {
-    VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV             = 0,
-    VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV          = 1,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-enum VkCopyAccelerationStructureModeNV {
-    VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_NV            = 0,
-    VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV          = 1,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-enum VkAccelerationStructureMemoryRequirementsTypeNV {
-    VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV            = 0,
-    VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV     = 1,
-    VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV    = 2,
-}
-
-@extension("VK_EXT_global_priority") // 175
-enum VkQueueGlobalPriorityEXT {
-    VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT                        = 128,
-    VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT                     = 256,
-    VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT                       = 512,
-    VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT                   = 1024,
-}
-
-@extension("VK_EXT_calibrated_timestamps") // 185
-enum VkTimeDomainEXT {
-    VK_TIME_DOMAIN_DEVICE_EXT                               = 0,
-    VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT                      = 1,
-    VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT                  = 2,
-    VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT            = 3,
-}
-
-@extension("VK_AMD_memory_overallocation_behavior") // 190
-enum VkMemoryOverallocationBehaviorAMD {
-    VK_MEMORY_OVERALLOCATION_BEHAVIOR_DEFAULT_AMD           = 0,
-    VK_MEMORY_OVERALLOCATION_BEHAVIOR_ALLOWED_AMD           = 1,
-    VK_MEMORY_OVERALLOCATION_BEHAVIOR_DISALLOWED_AMD        = 2,
-}
-
-@extension("VK_KHR_driver_properties") // 197
-enum VkDriverIdKHR {
-    VK_DRIVER_ID_AMD_PROPRIETARY_KHR                        = 1,
-    VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR                        = 2,
-    VK_DRIVER_ID_MESA_RADV_KHR                              = 3,
-    VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR                     = 4,
-    VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR              = 5,
-    VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR                 = 6,
-    VK_DRIVER_ID_IMAGINATION_PROPRIETARY_KHR                = 7,
-    VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR                   = 8,
-    VK_DRIVER_ID_ARM_PROPRIETARY_KHR                        = 9,
-    VK_DRIVER_ID_GOOGLE_PASTEL_KHR                          = 10,
-}
-
-/////////////////
-//  Bitfields  //
-/////////////////
-
-/// Queue capabilities
-type VkFlags VkQueueFlags
-bitfield VkQueueFlagBits {
-    VK_QUEUE_GRAPHICS_BIT                                   = 0x00000001,    /// Queue supports graphics operations
-    VK_QUEUE_COMPUTE_BIT                                    = 0x00000002,    /// Queue supports compute operations
-    VK_QUEUE_TRANSFER_BIT                                   = 0x00000004,    /// Queue supports transfer operations
-    VK_QUEUE_SPARSE_BINDING_BIT                             = 0x00000008,    /// Queue supports sparse resource memory management operations
-
-    //@vulkan1_1
-    VK_QUEUE_PROTECTED_BIT                                  = 0x00000010,
-}
-
-/// Memory properties passed into vkAllocMemory().
-type VkFlags VkMemoryPropertyFlags
-bitfield VkMemoryPropertyFlagBits {
-    VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT                     = 0x00000001,
-    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT                     = 0x00000002,
-    VK_MEMORY_PROPERTY_HOST_COHERENT_BIT                    = 0x00000004,
-    VK_MEMORY_PROPERTY_HOST_CACHED_BIT                      = 0x00000008,
-    VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT                 = 0x00000010,
-
-    //@vulkan1_1
-    VK_MEMORY_PROPERTY_PROTECTED_BIT                        = 0x00000020,
-}
-
-/// Memory heap flags
-type VkFlags VkMemoryHeapFlags
-bitfield VkMemoryHeapFlagBits {
-    VK_MEMORY_HEAP_DEVICE_LOCAL_BIT                         = 0x00000001,
-
-    //@vulkan1_1
-    VK_MEMORY_HEAP_MULTI_INSTANCE_BIT                       = 0x00000002,
-
-    //@extension("VK_KHR_device_group_creation") // 71
-    VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHR                   = 0x00000002,
-}
-
-/// Access flags
-type VkFlags VkAccessFlags
-bitfield VkAccessFlagBits {
-    VK_ACCESS_INDIRECT_COMMAND_READ_BIT                     = 0x00000001,
-    VK_ACCESS_INDEX_READ_BIT                                = 0x00000002,
-    VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT                     = 0x00000004,
-    VK_ACCESS_UNIFORM_READ_BIT                              = 0x00000008,
-    VK_ACCESS_INPUT_ATTACHMENT_READ_BIT                     = 0x00000010,
-    VK_ACCESS_SHADER_READ_BIT                               = 0x00000020,
-    VK_ACCESS_SHADER_WRITE_BIT                              = 0x00000040,
-    VK_ACCESS_COLOR_ATTACHMENT_READ_BIT                     = 0x00000080,
-    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT                    = 0x00000100,
-    VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT             = 0x00000200,
-    VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT            = 0x00000400,
-    VK_ACCESS_TRANSFER_READ_BIT                             = 0x00000800,
-    VK_ACCESS_TRANSFER_WRITE_BIT                            = 0x00001000,
-    VK_ACCESS_HOST_READ_BIT                                 = 0x00002000,
-    VK_ACCESS_HOST_WRITE_BIT                                = 0x00004000,
-    VK_ACCESS_MEMORY_READ_BIT                               = 0x00008000,
-    VK_ACCESS_MEMORY_WRITE_BIT                              = 0x00010000,
-
-    //@extension("VK_NVX_device_generated_commands") // 87
-    VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX                  = 0x00020000,
-    VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX                 = 0x00040000,
-
-    //@extension("VK_EXT_blend_operation_advanced") // 149
-    VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT     = 0x00080000,
-
-    //@extension("VK_EXT_conditional_rendering") // 82
-    VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT            = 0x00100000,
-
-    //@extension("VK_NV_shading_rate_image") // 165
-    VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV                = 0x00800000,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV            = 0x00200000,
-    VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV           = 0x00400000,
-
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT             = 0x01000000,
-
-    //@extension("VK_EXT_transform_feedback") // 29
-    VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT              = 0x02000000,
-    VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT       = 0x04000000,
-    VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT      = 0x08000000,
-}
-
-/// Buffer usage flags
-type VkFlags VkBufferUsageFlags
-bitfield VkBufferUsageFlagBits {
-    VK_BUFFER_USAGE_TRANSFER_SRC_BIT                        = 0x00000001,    /// Can be used as a source of transfer operations
-    VK_BUFFER_USAGE_TRANSFER_DST_BIT                        = 0x00000002,    /// Can be used as a destination of transfer operations
-    VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT                = 0x00000004,    /// Can be used as TBO
-    VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT                = 0x00000008,    /// Can be used as IBO
-    VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT                      = 0x00000010,    /// Can be used as UBO
-    VK_BUFFER_USAGE_STORAGE_BUFFER_BIT                      = 0x00000020,    /// Can be used as SSBO
-    VK_BUFFER_USAGE_INDEX_BUFFER_BIT                        = 0x00000040,    /// Can be used as source of fixed function index fetch (index buffer)
-    VK_BUFFER_USAGE_VERTEX_BUFFER_BIT                       = 0x00000080,    /// Can be used as source of fixed function vertex fetch (VBO)
-    VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT                     = 0x00000100,    /// Can be the source of indirect parameters (e.g. indirect buffer, parameter buffer)
-
-    //@extension("VK_EXT_conditional_rendering") // 82
-    VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT           = 0x00000200,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_BUFFER_USAGE_RAY_TRACING_BIT_NV                      = 0x00000400,
-
-    //@extension("VK_EXT_transform_feedback") // 29
-    VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT           = 0x00000800,
-    VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT   = 0x00001000,
-}
-
-/// Buffer creation flags
-type VkFlags VkBufferCreateFlags
-bitfield VkBufferCreateFlagBits {
-    VK_BUFFER_CREATE_SPARSE_BINDING_BIT                     = 0x00000001,    /// Buffer should support sparse backing
-    VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT                   = 0x00000002,    /// Buffer should support sparse backing with partial residency
-    VK_BUFFER_CREATE_SPARSE_ALIASED_BIT                     = 0x00000004,    /// Buffer should support constent data access to physical memory blocks mapped into multiple locations of sparse buffers
-
-    //@vulkan1_1
-    VK_BUFFER_CREATE_PROTECTED_BIT                          = 0x00000008,
-}
-
-/// Shader stage flags
-type VkFlags VkShaderStageFlags
-bitfield VkShaderStageFlagBits {
-    VK_SHADER_STAGE_VERTEX_BIT                              = 0x00000001,
-    VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT                = 0x00000002,
-    VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT             = 0x00000004,
-    VK_SHADER_STAGE_GEOMETRY_BIT                            = 0x00000008,
-    VK_SHADER_STAGE_FRAGMENT_BIT                            = 0x00000010,
-    VK_SHADER_STAGE_COMPUTE_BIT                             = 0x00000020,
-    VK_SHADER_STAGE_ALL_GRAPHICS                            = 0x0000001F,
-
-    VK_SHADER_STAGE_ALL                                     = 0x7FFFFFFF,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_SHADER_STAGE_RAYGEN_BIT_NV                           = 0x00000100,
-    VK_SHADER_STAGE_ANY_HIT_BIT_NV                          = 0x00000200,
-    VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV                      = 0x00000400,
-    VK_SHADER_STAGE_MISS_BIT_NV                             = 0x00000800,
-    VK_SHADER_STAGE_INTERSECTION_BIT_NV                     = 0x00001000,
-    VK_SHADER_STAGE_CALLABLE_BIT_NV                         = 0x00002000,
-
-    //@extension("VK_NV_mesh_shader") // 203
-    VK_SHADER_STAGE_TASK_BIT_NV                             = 0x00000040,
-    VK_SHADER_STAGE_MESH_BIT_NV                             = 0x00000080,
-}
-
-/// Descriptor pool create flags
-type VkFlags VkDescriptorPoolCreateFlags
-bitfield VkDescriptorPoolCreateFlagBits {
-    VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT       = 0x00000001,
-
-    //@extension("VK_EXT_descriptor_indexing") // 162
-    VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT     = 0x00000002,
-}
-
-/// Descriptor pool reset flags
-type VkFlags VkDescriptorPoolResetFlags
-//bitfield VkDescriptorPoolResetFlagBits {
-//}
-
-/// Image usage flags
-type VkFlags VkImageUsageFlags
-bitfield VkImageUsageFlagBits {
-    VK_IMAGE_USAGE_TRANSFER_SRC_BIT                         = 0x00000001,    /// Can be used as a source of transfer operations
-    VK_IMAGE_USAGE_TRANSFER_DST_BIT                         = 0x00000002,    /// Can be used as a destination of transfer operations
-    VK_IMAGE_USAGE_SAMPLED_BIT                              = 0x00000004,    /// Can be sampled from (SAMPLED_IMAGE and COMBINED_IMAGE_SAMPLER descriptor types)
-    VK_IMAGE_USAGE_STORAGE_BIT                              = 0x00000008,    /// Can be used as storage image (STORAGE_IMAGE descriptor type)
-    VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT                     = 0x00000010,    /// Can be used as framebuffer color attachment
-    VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT             = 0x00000020,    /// Can be used as framebuffer depth/stencil attachment
-    VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT                 = 0x00000040,    /// Image data not needed outside of rendering
-    VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT                     = 0x00000080,    /// Can be used as framebuffer input attachment
-
-    //@extension("VK_NV_shading_rate_image") // 165
-    VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV                = 0x00000100,
-
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT             = 0x00000200,
-}
-
-/// Image creation flags
-type VkFlags VkImageCreateFlags
-bitfield VkImageCreateFlagBits {
-    VK_IMAGE_CREATE_SPARSE_BINDING_BIT                      = 0x00000001,    /// Image should support sparse backing
-    VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT                    = 0x00000002,    /// Image should support sparse backing with partial residency
-    VK_IMAGE_CREATE_SPARSE_ALIASED_BIT                      = 0x00000004,    /// Image should support constent data access to physical memory blocks mapped into multiple locations of sparse images
-    VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT                      = 0x00000008,    /// Allows image views to have different format than the base image
-    VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT                     = 0x00000010,    /// Allows creating image views with cube type from the created image
-
-    //@vulkan1_1
-    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT                 = 0x00000020,
-    VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT         = 0x00000040,
-    VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT         = 0x00000080,
-    VK_IMAGE_CREATE_EXTENDED_USAGE_BIT                      = 0x00000100,
-    VK_IMAGE_CREATE_DISJOINT_BIT                            = 0x00000200,
-    VK_IMAGE_CREATE_ALIAS_BIT                               = 0x00000400,
-    VK_IMAGE_CREATE_PROTECTED_BIT                           = 0x00000800,
-
-    //@extension("VK_KHR_maintenance1") // 70
-    VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR             = 0x00000020,
-
-    //@extension("VK_KHR_device_group") // 61
-    VK_IMAGE_CREATE_BIND_SFR_BIT_KHR                        = 0x00000040,
-
-    //@extension("VK_KHR_maintenance2") // 118
-    VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR     = 0x00000080,
-    VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR                  = 0x00000100,
-
-    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-    VK_IMAGE_CREATE_DISJOINT_BIT_KHR                        = 0x00000200,
-
-    //@extension("VK_KHR_bind_memory2") // 158
-    VK_IMAGE_CREATE_ALIAS_BIT_KHR                           = 0x00000400,
-
-    //@extension("VK_EXT_sample_locations") // 144
-    VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000,
-
-    //@extension("VK_NV_corner_sampled_image") // 51
-    VK_IMAGE_CREATE_CORNER_SAMPLED_BIT_NV                   = 0x00002000,
-
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT                      = 0x00004000,
-}
-
-/// Image view creation flags
-type VkFlags VkImageViewCreateFlags
-bitfield VkImageViewCreateFlagBits {
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT   = 0x00000001,
-}
-
-/// Pipeline creation flags
-type VkFlags VkPipelineCreateFlags
-bitfield VkPipelineCreateFlagBits {
-    VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT             = 0x00000001,
-    VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT                = 0x00000002,
-    VK_PIPELINE_CREATE_DERIVATIVE_BIT                       = 0x00000004,
-
-    //@vulkan1_1
-    VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT     = 0x00000008,
-    VK_PIPELINE_CREATE_DISPATCH_BASE                        = 0x00000010,
-
-    //@extension("VK_KHR_device_group") // 61
-    VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHR = 0x00000008,
-    VK_PIPELINE_CREATE_DISPATCH_BASE_KHR                    = 0x00000010,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_PIPELINE_CREATE_DEFER_COMPILE_BIT_NV                 = 0x00000020,
-}
-
-/// Color component flags
-type VkFlags VkColorComponentFlags
-bitfield VkColorComponentFlagBits {
-    VK_COLOR_COMPONENT_R_BIT                                = 0x00000001,
-    VK_COLOR_COMPONENT_G_BIT                                = 0x00000002,
-    VK_COLOR_COMPONENT_B_BIT                                = 0x00000004,
-    VK_COLOR_COMPONENT_A_BIT                                = 0x00000008,
-}
-
-/// Fence creation flags
-type VkFlags VkFenceCreateFlags
-bitfield VkFenceCreateFlagBits {
-    VK_FENCE_CREATE_SIGNALED_BIT                            = 0x00000001,
-}
-
-/// Semaphore creation flags
-type VkFlags VkSemaphoreCreateFlags
-//bitfield VkSemaphoreCreateFlagBits {
-//}
-
-/// Format capability flags
-type VkFlags VkFormatFeatureFlags
-bitfield VkFormatFeatureFlagBits {
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT                     = 0x00000001,    /// Format can be used for sampled images (SAMPLED_IMAGE and COMBINED_IMAGE_SAMPLER descriptor types)
-    VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT                     = 0x00000002,    /// Format can be used for storage images (STORAGE_IMAGE descriptor type)
-    VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT              = 0x00000004,    /// Format supports atomic operations in case it's used for storage images
-    VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT              = 0x00000008,    /// Format can be used for uniform texel buffers (TBOs)
-    VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT              = 0x00000010,    /// Format can be used for storage texel buffers (IBOs)
-    VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT       = 0x00000020,    /// Format supports atomic operations in case it's used for storage texel buffers
-    VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT                     = 0x00000040,    /// Format can be used for vertex buffers (VBOs)
-    VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT                  = 0x00000080,    /// Format can be used for color attachment images
-    VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT            = 0x00000100,    /// Format supports blending in case it's used for color attachment images
-    VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT          = 0x00000200,    /// Format can be used for depth/stencil attachment images
-    VK_FORMAT_FEATURE_BLIT_SRC_BIT                          = 0x00000400,    /// Format can be used as the source image of blits with vkCommandBlitImage
-    VK_FORMAT_FEATURE_BLIT_DST_BIT                          = 0x00000800,    /// Format can be used as the destination image of blits with vkCommandBlitImage
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT       = 0x00001000,
-
-    //@vulkan1_1
-    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT                      = 0x00004000,
-    VK_FORMAT_FEATURE_TRANSFER_DST_BIT                      = 0x00008000,
-    VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT                                                   = 0x00020000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT                              = 0x00040000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT             = 0x00080000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT             = 0x00100000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT   = 0x00200000,
-    VK_FORMAT_FEATURE_DISJOINT_BIT                                                                  = 0x00400000,
-    VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT                                                    = 0x00800000,
-
-    //@extension("VK_IMG_filter_cubic") // 16
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG    = 0x00002000,
-
-    //@extension("VK_KHR_maintenance1") // 70
-    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR                  = 0x00004000,
-    VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR                  = 0x00008000,
-
-    //@extension("VK_EXT_sampler_filter_minmax") // 131
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT   = 0x00010000,
-
-    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-    VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR                                                   = 0x00020000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR                              = 0x00040000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR             = 0x00080000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR             = 0x00100000,
-    VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR   = 0x00200000,
-    VK_FORMAT_FEATURE_DISJOINT_BIT_KHR                                                                  = 0x00400000,
-    VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR                                                    = 0x00800000,
-}
-
-/// Query control flags
-type VkFlags VkQueryControlFlags
-bitfield VkQueryControlFlagBits {
-    VK_QUERY_CONTROL_PRECISE_BIT                            = 0x00000001,
-}
-
-/// Query result flags
-type VkFlags VkQueryResultFlags
-bitfield VkQueryResultFlagBits {
-    VK_QUERY_RESULT_64_BIT                                  = 0x00000001,   /// Results of the queries are written to the destination buffer as 64-bit values
-    VK_QUERY_RESULT_WAIT_BIT                                = 0x00000002,   /// Results of the queries are waited on before proceeding with the result copy
-    VK_QUERY_RESULT_WITH_AVAILABILITY_BIT                   = 0x00000004,   /// Besides the results of the query, the availability of the results is also written
-    VK_QUERY_RESULT_PARTIAL_BIT                             = 0x00000008,   /// Copy the partial results of the query even if the final results aren't available
-}
-
-/// Shader module creation flags
-type VkFlags VkShaderModuleCreateFlags
-//bitfield VkShaderModuleCreateFlagBits {
-//}
-
-/// Event creation flags
-type VkFlags VkEventCreateFlags
-//bitfield VkEventCreateFlagBits {
-//}
-
-/// Command buffer usage flags
-type VkFlags VkCommandBufferUsageFlags
-bitfield VkCommandBufferUsageFlagBits {
-    VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT             = 0x00000001,
-    VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT        = 0x00000002,
-    VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT            = 0x00000004,
-}
-
-/// Pipeline statistics flags
-type VkFlags VkQueryPipelineStatisticFlags
-bitfield VkQueryPipelineStatisticFlagBits {
-    VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT                     = 0x00000001,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT                   = 0x00000002,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT                   = 0x00000004,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT                 = 0x00000008,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT                  = 0x00000010,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT                        = 0x00000020,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT                         = 0x00000040,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT                 = 0x00000080,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT         = 0x00000100,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT  = 0x00000200,  /// Optional
-    VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT                  = 0x00000400,  /// Optional
-}
-
-/// Memory mapping flags
-type VkFlags VkMemoryMapFlags
-//bitfield VkMemoryMapFlagBits {
-//}
-
-/// Bitfield of image aspects
-type VkFlags VkImageAspectFlags
-bitfield VkImageAspectFlagBits {
-    VK_IMAGE_ASPECT_COLOR_BIT                               = 0x00000001,
-    VK_IMAGE_ASPECT_DEPTH_BIT                               = 0x00000002,
-    VK_IMAGE_ASPECT_STENCIL_BIT                             = 0x00000004,
-    VK_IMAGE_ASPECT_METADATA_BIT                            = 0x00000008,
-
-    //@vulkan1_1
-    VK_IMAGE_ASPECT_PLANE_0_BIT                             = 0x00000010,
-    VK_IMAGE_ASPECT_PLANE_1_BIT                             = 0x00000020,
-    VK_IMAGE_ASPECT_PLANE_2_BIT                             = 0x00000040,
-
-    //@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-    VK_IMAGE_ASPECT_PLANE_0_BIT_KHR                         = 0x00000010,
-    VK_IMAGE_ASPECT_PLANE_1_BIT_KHR                         = 0x00000020,
-    VK_IMAGE_ASPECT_PLANE_2_BIT_KHR                         = 0x00000040,
-
-    //@extension("VK_EXT_transform_feedback") // 29
-    VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT                  = 0x00000080,
-    VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT                  = 0x00000100,
-    VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT                  = 0x00000200,
-    VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT                  = 0x00000400,
-}
-
-/// Sparse memory bind flags
-type VkFlags VkSparseMemoryBindFlags
-bitfield VkSparseMemoryBindFlagBits {
-    VK_SPARSE_MEMORY_BIND_METADATA_BIT                      = 0x00000001,
-}
-
-/// Sparse image memory requirements flags
-type VkFlags VkSparseImageFormatFlags
-bitfield VkSparseImageFormatFlagBits {
-    VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT                  = 0x00000001,  /// Image uses a single miptail region for all array slices
-    VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT                = 0x00000002,  /// Image requires mip levels to be an exact multiple of the sparse iamge block size for non-mip-tail levels.
-    VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT          = 0x00000004,  /// Image uses a non-standard sparse block size
-}
-
-/// Pipeline stages
-type VkFlags VkPipelineStageFlags
-bitfield VkPipelineStageFlagBits {
-    VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT                       = 0x00000001,  /// Before subsequent commands are processed
-    VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT                     = 0x00000002,  /// Draw/DispatchIndirect command fetch
-    VK_PIPELINE_STAGE_VERTEX_INPUT_BIT                      = 0x00000004,  /// Vertex/index fetch
-    VK_PIPELINE_STAGE_VERTEX_SHADER_BIT                     = 0x00000008,  /// Vertex shading
-    VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT       = 0x00000010,  /// Tessellation control shading
-    VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT    = 0x00000020,  /// Tessellation evaluation shading
-    VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT                   = 0x00000040,  /// Geometry shading
-    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT                   = 0x00000080,  /// Fragment shading
-    VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT              = 0x00000100,  /// Early fragment (depth/stencil) tests
-    VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT               = 0x00000200,  /// Late fragment (depth/stencil) tests
-    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT           = 0x00000400,  /// Color attachment writes
-    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT                    = 0x00000800,  /// Compute shading
-    VK_PIPELINE_STAGE_TRANSFER_BIT                          = 0x00001000,  /// Transfer/copy operations
-    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT                    = 0x00002000,
-    VK_PIPELINE_STAGE_HOST_BIT                              = 0x00004000,  /// Indicates host (CPU) is a source/sink of the dependency
-
-    VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT                      = 0x00008000,  /// All stages of the graphics pipeline
-    VK_PIPELINE_STAGE_ALL_COMMANDS_BIT                      = 0x00010000,  /// All graphics, compute, copy, and transition commands
-
-    //@extension("VK_NVX_device_generated_commands") // 87
-    VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX               = 0x00020000,
-
-    //@extension("VK_EXT_conditional_rendering") // 82
-    VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT         = 0x00040000,
-
-    //@extension("VK_NV_mesh_shader") // 203
-    VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV                    = 0x00080000,
-    VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV                    = 0x00100000,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_PIPELINE_STAGE_RAY_TRACING_BIT_NV                    = 0x00200000,
-
-    //@extension("VK_NV_shading_rate_image") // 165
-    VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV             = 0x00400000,
-
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT      = 0x00800000,
-
-    //@extension("VK_EXT_transform_feedback") // 29
-    VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT            = 0x01000000,
-
-    //@extension("VK_NV_ray_tracing") // 166
-    VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV   = 0x02000000,
-}
-
-/// Render pass attachment description flags
-type VkFlags VkAttachmentDescriptionFlags
-bitfield VkAttachmentDescriptionFlagBits {
-    VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT                 = 0x00000001,  /// The attachment may alias physical memory of another attachment in the same renderpass
-}
-
-/// Subpass description flags
-type VkFlags VkSubpassDescriptionFlags
-bitfield VkSubpassDescriptionFlagBits {
-    //@extension("VK_NVX_multiview_per_view_attributes") // 98
-    VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX      = 0x00000001,
-    VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002,
-}
-
-/// Command pool creation flags
-type VkFlags VkCommandPoolCreateFlags
-bitfield VkCommandPoolCreateFlagBits {
-    VK_COMMAND_POOL_CREATE_TRANSIENT_BIT                    = 0x00000001,  /// Command buffers have a short lifetime
-    VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT         = 0x00000002,  /// Command buffers may release their memory individually
-
-    //@vulkan1_1
-    VK_COMMAND_POOL_CREATE_PROTECTED_BIT                    = 0x00000004,
-}
-
-/// Command pool reset flags
-type VkFlags VkCommandPoolResetFlags
-bitfield VkCommandPoolResetFlagBits {
-    VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT             = 0x00000001,  /// Release resources owned by the pool
-}
-
-type VkFlags VkCommandBufferResetFlags
-bitfield VkCommandBufferResetFlagBits {
-    VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT           = 0x00000001,  /// Release resources owned by the buffer
-}
-
-type VkFlags VkSampleCountFlags
-bitfield VkSampleCountFlagBits {
-    VK_SAMPLE_COUNT_1_BIT                                   = 0x00000001,
-    VK_SAMPLE_COUNT_2_BIT                                   = 0x00000002,
-    VK_SAMPLE_COUNT_4_BIT                                   = 0x00000004,
-    VK_SAMPLE_COUNT_8_BIT                                   = 0x00000008,
-    VK_SAMPLE_COUNT_16_BIT                                  = 0x00000010,
-    VK_SAMPLE_COUNT_32_BIT                                  = 0x00000020,
-    VK_SAMPLE_COUNT_64_BIT                                  = 0x00000040,
-}
-
-type VkFlags VkStencilFaceFlags
-bitfield VkStencilFaceFlagBits {
-    VK_STENCIL_FACE_FRONT_BIT                               = 0x00000001,   /// Front face
-    VK_STENCIL_FACE_BACK_BIT                                = 0x00000002,   /// Back face
-    VK_STENCIL_FRONT_AND_BACK                               = 0x00000003,
-}
-
-/// Instance creation flags
-type VkFlags VkInstanceCreateFlags
-//bitfield VkInstanceCreateFlagBits {
-//}
-
-/// Device creation flags
-type VkFlags VkDeviceCreateFlags
-//bitfield VkDeviceCreateFlagBits {
-//}
-
-/// Device queue creation flags
-type VkFlags VkDeviceQueueCreateFlags
-@vulkan1_1
-bitfield VkDeviceQueueCreateFlagBits {
-    VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT                    = 0x00000001,
-}
-
-/// Query pool creation flags
-type VkFlags VkQueryPoolCreateFlags
-//bitfield VkQueryPoolCreateFlagBits {
-//}
-
-/// Buffer view creation flags
-type VkFlags VkBufferViewCreateFlags
-//bitfield VkBufferViewCreateFlagBits {
-//}
-
-/// Pipeline cache creation flags
-type VkFlags VkPipelineCacheCreateFlags
-//bitfield VkPipelineCacheCreateFlagBits {
-//}
-
-/// Pipeline shader stage creation flags
-type VkFlags VkPipelineShaderStageCreateFlags
-//bitfield VkPipelineShaderStageCreateFlagBits {
-//}
-
-/// Descriptor set layout creation flags
-type VkFlags VkDescriptorSetLayoutCreateFlags
-bitfield VkDescriptorSetLayoutCreateFlagBits {
-    //@extension("VK_KHR_push_descriptor") // 81
-    VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR     = 0x00000001,
-
-    //@extension("VK_EXT_descriptor_indexing") // 162
-    VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT  = 0x00000002,
-}
-
-/// Pipeline vertex input state creation flags
-type VkFlags VkPipelineVertexInputStateCreateFlags
-//bitfield VkPipelineVertexInputStateCreateFlagBits {
-//}
-
-/// Pipeline input assembly state creation flags
-type VkFlags VkPipelineInputAssemblyStateCreateFlags
-//bitfield VkPipelineInputAssemblyStateCreateFlagBits {
-//}
-
-/// Tessellation state creation flags
-type VkFlags VkPipelineTessellationStateCreateFlags
-//bitfield VkPipelineTessellationStateCreateFlagBits {
-//}
-
-/// Viewport state creation flags
-type VkFlags VkPipelineViewportStateCreateFlags
-//bitfield VkPipelineViewportStateCreateFlagBits {
-//}
-
-/// Rasterization state creation flags
-type VkFlags VkPipelineRasterizationStateCreateFlags
-//bitfield VkPipelineRasterizationStateCreateFlagBits {
-//}
-
-/// Multisample state creation flags
-type VkFlags VkPipelineMultisampleStateCreateFlags
-//bitfield VkPipelineMultisampleStateCreateFlagBits {
-//}
-
-/// Color blend state creation flags
-type VkFlags VkPipelineColorBlendStateCreateFlags
-//bitfield VkPipelineColorBlendStateCreateFlagBits {
-//}
-
-/// Depth/stencil state creation flags
-type VkFlags VkPipelineDepthStencilStateCreateFlags
-//bitfield VkPipelineDepthStencilStateCreateFlagBits {
-//}
-
-/// Dynamic state creation flags
-type VkFlags VkPipelineDynamicStateCreateFlags
-//bitfield VkPipelineDynamicStateCreateFlagBits {
-//}
-
-/// Pipeline layout creation flags
-type VkFlags VkPipelineLayoutCreateFlags
-//bitfield VkPipelineLayoutCreateFlagBits {
-//}
-
-/// Sampler creation flags
-type VkFlags VkSamplerCreateFlags
-bitfield VkSamplerCreateFlagBits {
-    //@extension("VK_EXT_fragment_density_map") // 219
-    VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT                        = 0x00000001,
-    VK_SAMPLER_CREATE_SUBSAMPLED_COARSE_RECONSTRUCTION_BIT_EXT  = 0x00000002,
-}
-
-/// Render pass creation flags
-type VkFlags VkRenderPassCreateFlags
-//bitfield VkRenderPassCreateFlagBits {
-//}
-
-/// Framebuffer creation flags
-type VkFlags VkFramebufferCreateFlags
-//bitfield VkFramebufferCreateFlagBits {
-//}
-
-/// Dependency flags
-type VkFlags VkDependencyFlags
-bitfield VkDependencyFlagBits {
-    VK_DEPENDENCY_BY_REGION_BIT                             = 0x00000001,
-
-    //@vulkan1_1
-    VK_DEPENDENCY_DEVICE_GROUP_BIT                          = 0x00000004,
-    VK_DEPENDENCY_VIEW_LOCAL_BIT                            = 0x00000002,
-
-    //@extension("VK_KHR_multiview") // 54
-    VK_DEPENDENCY_VIEW_LOCAL_BIT_KHR                        = 0x00000002,
-
-    //@extension("VK_KHR_device_group") // 61
-    VK_DEPENDENCY_DEVICE_GROUP_BIT_KHR                      = 0x00000004,
-}
-
-/// Cull mode flags
-type VkFlags VkCullModeFlags
-bitfield VkCullModeFlagBits {
-    VK_CULL_MODE_NONE                                       = 0x00000000,
-    VK_CULL_MODE_FRONT_BIT                                  = 0x00000001,
-    VK_CULL_MODE_BACK_BIT                                   = 0x00000002,
-    VK_CULL_MODE_FRONT_AND_BACK                             = 0x00000003,
-}
-
-//@vulkan1_1 flags
-
-/// Subgroup feature flags
-type VkFlags VkSubgroupFeatureFlags
-bitfield VkSubgroupFeatureFlagBits {
-    VK_SUBGROUP_FEATURE_BASIC_BIT                           = 0x00000001,
-    VK_SUBGROUP_FEATURE_VOTE_BIT                            = 0x00000002,
-    VK_SUBGROUP_FEATURE_ARITHMETIC_BIT                      = 0x00000004,
-    VK_SUBGROUP_FEATURE_BALLOT_BIT                          = 0x00000008,
-    VK_SUBGROUP_FEATURE_SHUFFLE_BIT                         = 0x00000010,
-    VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT                = 0x00000020,
-    VK_SUBGROUP_FEATURE_CLUSTERED_BIT                       = 0x00000040,
-    VK_SUBGROUP_FEATURE_QUAD_BIT                            = 0x00000080,
-
-    //@extension("VK_NV_shader_subgroup_partitioned") // 199
-    VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV                  = 0x00000100,
-}
-
-/// Peer memory feature flags
-type VkFlags VkPeerMemoryFeatureFlags
-bitfield VkPeerMemoryFeatureFlagBits {
-    VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT                     = 0x00000001,
-    VK_PEER_MEMORY_FEATURE_COPY_DST_BIT                     = 0x00000002,
-    VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT                  = 0x00000004,
-    VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT                  = 0x00000008,
-}
-
-/// Memory allocation flags
-type VkFlags VkMemoryAllocateFlags
-bitfield VkMemoryAllocateFlagBits {
-    VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT                      = 0x00000001,
-}
-
-type VkFlags VkCommandPoolTrimFlags
-//bitfield VkCommandPoolTrimFlagBits {
-//}
-
-type VkFlags VkDescriptorUpdateTemplateCreateFlags
-//bitfield VkDescriptorUpdateTemplateCreateFlagBits {
-//}
-
-/// External memory handle type flags
-type VkFlags VkExternalMemoryHandleTypeFlags
-bitfield VkExternalMemoryHandleTypeFlagBits {
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT            = 0x00000001,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT         = 0x00000002,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT     = 0x00000004,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT        = 0x00000008,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT    = 0x00000010,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT           = 0x00000020,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT       = 0x00000040,
-
-    //@extension("VK_EXT_external_memory_host") // 179
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT              = 0x00000080,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT   = 0x00000100,
-
-    //@extension("VK_EXT_external_memory_dma_buf") // 126
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT                      = 0x00000200,
-
-    //@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID  = 0x00000400,
-}
-
-/// External memory feature flags
-type VkFlags VkExternalMemoryFeatureFlags
-bitfield VkExternalMemoryFeatureFlagBits {
-    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT           = 0x00000001,
-    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT               = 0x00000002,
-    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT               = 0x00000004,
-}
-
-/// External fence handle type flags
-type VkFlags VkExternalFenceHandleTypeFlags
-bitfield VkExternalFenceHandleTypeFlagBits {
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT             = 0x00000001,
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT          = 0x00000002,
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT      = 0x00000004,
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT               = 0x00000008,
-}
-
-/// External fence feature flags
-type VkFlags VkExternalFenceFeatureFlags
-bitfield VkExternalFenceFeatureFlagBits {
-    VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT                = 0x00000001,
-    VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT                = 0x00000002,
-}
-
-/// Fence import flags
-type VkFlags VkFenceImportFlags
-bitfield VkFenceImportFlagBits {
-    VK_FENCE_IMPORT_TEMPORARY_BIT                           = 0x00000001,
-}
-
-/// Semaphore import flags
-type VkFlags VkSemaphoreImportFlags
-bitfield VkSemaphoreImportFlagBits {
-    VK_SEMAPHORE_IMPORT_TEMPORARY_BIT                       = 0x00000001,
-}
-
-/// External semaphore handle type flags
-type VkFlags VkExternalSemaphoreHandleTypeFlags
-bitfield VkExternalSemaphoreHandleTypeFlagBits {
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT         = 0x00000001,
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT      = 0x00000002,
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT  = 0x00000004,
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT       = 0x00000008,
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT           = 0x00000010,
-}
-
-/// External semaphore feature flags
-type VkFlags VkExternalSemaphoreFeatureFlags
-bitfield VkExternalSemaphoreFeatureFlagBits {
-    VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT            = 0x00000001,
-    VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT            = 0x00000002,
-}
-
-@extension("VK_KHR_surface") // 1
-type VkFlags VkSurfaceTransformFlagsKHR
-@extension("VK_KHR_surface") // 1
-bitfield VkSurfaceTransformFlagBitsKHR {
-    VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR                       = 0x00000001,
-    VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR                      = 0x00000002,
-    VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR                     = 0x00000004,
-    VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR                     = 0x00000008,
-    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR              = 0x00000010,
-    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR    = 0x00000020,
-    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR   = 0x00000040,
-    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR   = 0x00000080,
-    VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR                        = 0x00000100,
-}
-
-@extension("VK_KHR_surface") // 1
-type VkFlags VkCompositeAlphaFlagsKHR
-@extension("VK_KHR_surface") // 1
-bitfield VkCompositeAlphaFlagBitsKHR {
-    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR                       = 0x00000001,
-    VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR               = 0x00000002,
-    VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR              = 0x00000004,
-    VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR                      = 0x00000008,
-}
-
-@extension("VK_KHR_swapchain") // 2
-type VkFlags VkSwapchainCreateFlagsKHR
-@extension("VK_KHR_swapchain") // 2
-bitfield VkSwapchainCreateFlagBitsKHR {
-    //@vulkan1_1
-    VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = 0x00000001,
-    VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR                   = 0x00000002,
-
-    //@extension("VK_KHR_swapchain_mutable_format") // 201
-    VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR              = 0x00000004,
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-type VkFlags VkDeviceGroupPresentModeFlagsKHR
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-bitfield VkDeviceGroupPresentModeFlagBitsKHR {
-    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR              = 0x00000001,
-    VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHR             = 0x00000002,
-    VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHR                = 0x00000004,
-    VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHR = 0x00000008,
-}
-
-@extension("VK_KHR_display") // 3
-type VkFlags VkDisplayPlaneAlphaFlagsKHR
-@extension("VK_KHR_display") // 3
-bitfield VkDisplayPlaneAlphaFlagBitsKHR {
-    VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR                   = 0x00000001,
-    VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR                   = 0x00000002,
-    VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR                = 0x00000004,
-    VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR  = 0x00000008,
-}
-
-@extension("VK_KHR_display") // 3
-type VkFlags VkDisplaySurfaceCreateFlagsKHR
-//@extension("VK_KHR_display") // 3
-//bitfield VkDisplaySurfaceCreateFlagBitsKHR {
-//}
-
-@extension("VK_KHR_display") // 3
-type VkFlags VkDisplayModeCreateFlagsKHR
-//@extension("VK_KHR_display") // 3
-//bitfield VkDisplayModeCreateFlagBitsKHR {
-//}
-
-@extension("VK_KHR_xlib_surface") // 5
-type VkFlags VkXlibSurfaceCreateFlagsKHR
-//@extension("VK_KHR_xlib_surface") // 5
-//bitfield VkXlibSurfaceCreateFlagBitsKHR {
-//}
-
-@extension("VK_KHR_xcb_surface") // 6
-type VkFlags VkXcbSurfaceCreateFlagsKHR
-//@extension("VK_KHR_xcb_surface") // 6
-//bitfield VkXcbSurfaceCreateFlagBitsKHR {
-//}
-
-@extension("VK_KHR_wayland_surface") // 7
-type VkFlags VkWaylandSurfaceCreateFlagsKHR
-//@extension("VK_KHR_wayland_surface") // 7
-//bitfield VkWaylandSurfaceCreateFlagBitsKHR {
-//}
-
-@extension("VK_KHR_android_surface") // 9
-type VkFlags VkAndroidSurfaceCreateFlagsKHR
-//@extension("VK_KHR_android_surface") // 9
-//bitfield VkAndroidSurfaceCreateFlagBitsKHR {
-//}
-
-@extension("VK_KHR_win32_surface") // 10
-type VkFlags VkWin32SurfaceCreateFlagsKHR
-//@extension("VK_KHR_win32_surface") // 10
-//bitfield VkWin32SurfaceCreateFlagBitsKHR {
-//}
-
-@extension("VK_ANDROID_native_buffer") // 11
-type VkFlags VkSwapchainImageUsageFlagsANDROID
-@extension("VK_ANDROID_native_buffer") // 11
-bitfield VkSwapchainImageUsageFlagBitsANDROID {
-    VK_SWAPCHAIN_IMAGE_USAGE_FLAGS_SHARED_BIT_ANDROID = 0x00000001,
-}
-
-@extension("VK_EXT_debug_report") // 12
-type VkFlags VkDebugReportFlagsEXT
-@extension("VK_EXT_debug_report") // 12
-bitfield VkDebugReportFlagBitsEXT {
-    VK_DEBUG_REPORT_INFORMATION_BIT_EXT                     = 0x00000001,
-    VK_DEBUG_REPORT_WARNING_BIT_EXT                         = 0x00000002,
-    VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT             = 0x00000004,
-    VK_DEBUG_REPORT_ERROR_BIT_EXT                           = 0x00000008,
-    VK_DEBUG_REPORT_DEBUG_BIT_EXT                           = 0x00000010,
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-type VkFlags VkPipelineRasterizationStateStreamCreateFlagsEXT
-//@extension("VK_EXT_transform_feedback") // 29
-//bitfield VkPipelineRasterizationStateStreamCreateFlagBitsEXT {
-//}
-
-@extension("VK_NV_external_memory_capabilities") // 56
-type VkFlags VkExternalMemoryHandleTypeFlagsNV
-@extension("VK_NV_external_memory_capabilities") // 56
-bitfield VkExternalMemoryHandleTypeFlagBitsNV {
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV      = 0x00000001,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV  = 0x00000002,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV       = 0x00000004,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV   = 0x00000008,
-}
-
-@extension("VK_NV_external_memory_capabilities") // 56
-type VkFlags VkExternalMemoryFeatureFlagsNV
-@extension("VK_NV_external_memory_capabilities") // 56
-bitfield VkExternalMemoryFeatureFlagBitsNV {
-    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV        = 0x00000001,
-    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV            = 0x00000002,
-    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV            = 0x00000004,
-}
-
-@extension("VK_KHR_device_group") // 61
-type VkFlags VkPeerMemoryFeatureFlagsKHR
-@extension("VK_KHR_device_group") // 61
-bitfield VkPeerMemoryFeatureFlagBitsKHR {
-    VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHR                 = 0x00000001,
-    VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHR                 = 0x00000002,
-    VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHR              = 0x00000004,
-    VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHR              = 0x00000008,
-}
-
-@extension("VK_KHR_device_group") // 61
-type VkFlags VkMemoryAllocateFlagsKHR
-@extension("VK_KHR_device_group") // 61
-bitfield VkMemoryAllocateFlagBitsKHR {
-    VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHR                  = 0x00000001,
-}
-
-@extension("VK_NN_vi_surface") // 63
-type VkFlags VkViSurfaceCreateFlagsNN
-//@extension("VK_NN_vi_surface") // 63
-//bitfield VkViSurfaceCreateFlagBitsNN {
-//}
-
-@extension("VK_KHR_maintenance1") // 70
-type VkFlags VkCommandPoolTrimFlagsKHR
-//@extension("VK_KHR_maintenance1") // 70
-//bitfield VkCommandPoolTrimFlagBitsKHR {
-//}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-type VkFlags VkExternalMemoryHandleTypeFlagsKHR
-@extension("VK_KHR_external_memory_capabilities") // 72
-bitfield VkExternalMemoryHandleTypeFlagBitsKHR {
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR            = 0x00000001,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR         = 0x00000002,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR     = 0x00000004,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHR        = 0x00000008,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHR    = 0x00000010,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHR           = 0x00000020,
-    VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHR       = 0x00000040,
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-type VkFlags VkExternalMemoryFeatureFlagsKHR
-@extension("VK_KHR_external_memory_capabilities") // 72
-bitfield VkExternalMemoryFeatureFlagBitsKHR {
-    VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR           = 0x00000001,
-    VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR               = 0x00000002,
-    VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR               = 0x00000004,
-}
-
-@extension("VK_KHR_external_semaphore_capabilities") // 77
-type VkFlags VkExternalSemaphoreHandleTypeFlagsKHR
-@extension("VK_KHR_external_semaphore_capabilities") // 77
-bitfield VkExternalSemaphoreHandleTypeFlagBitsKHR {
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR         = 0x00000001
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR      = 0x00000002
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR  = 0x00000004
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHR       = 0x00000008
-    VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHR          = 0x00000010
-}
-
-@extension("VK_KHR_external_semaphore_capabilities") // 77
-type VkFlags VkExternalSemaphoreFeatureFlagsKHR
-@extension("VK_KHR_external_semaphore_capabilities") // 77
-bitfield VkExternalSemaphoreFeatureFlagBitsKHR {
-    VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR            = 0x00000001,
-    VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR            = 0x00000002,
-}
-
-@extension("VK_KHR_external_semaphore") // 78
-type VkFlags VkSemaphoreImportFlagsKHR
-@extension("VK_KHR_external_semaphore") // 78
-bitfield VkSemaphoreImportFlagBitsKHR {
-    VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR                       = 0x00000001,
-}
-
-@extension("VK_EXT_conditional_rendering") // 82
-type VkFlags VkConditionalRenderingFlagsEXT
-@extension("VK_EXT_conditional_rendering") // 82
-bitfield VkConditionalRenderingFlagBitsEXT {
-    VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT                   = 0x00000001,
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-type VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR
-//@extension("VK_KHR_descriptor_update_template") // 86
-//bitfield VkDescriptorUpdateTemplateCreateFlagBitsKHR {
-//}
-
-@extension("VK_NVX_device_generated_commands") // 87
-type VkFlags VkIndirectCommandsLayoutUsageFlagsNVX
-@extension("VK_NVX_device_generated_commands") // 87
-bitfield VkIndirectCommandsLayoutUsageFlagBitsNVX {
-    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX   = 0x00000001,
-    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX      = 0x00000002,
-    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX      = 0x00000004,
-    VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX     = 0x00000008,
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-type VkFlags VkObjectEntryUsageFlagsNVX
-@extension("VK_NVX_device_generated_commands") // 87
-bitfield VkObjectEntryUsageFlagBitsNVX {
-    VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX                  = 0x00000001,
-    VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX                   = 0x00000002,
-}
-
-@extension("VK_EXT_display_surface_counter") // 91
-type VkFlags VkSurfaceCounterFlagsEXT
-@extension("VK_EXT_display_surface_counter") // 91
-bitfield VkSurfaceCounterFlagBitsEXT {
-    VK_SURFACE_COUNTER_VBLANK_EXT                           = 0x00000001,
-}
-
-@extension("VK_NV_viewport_swizzle") // 99
-type VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV
-//@extension("VK_NV_viewport_swizzle") // 99
-//bitfield VkPipelineViewportSwizzleStateCreateFlagBitsNV {
-//}
-
-@extension("VK_EXT_discard_rectangles") // 100
-type VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT
-//@extension("VK_EXT_discard_rectangles") // 100
-//bitfield VkPipelineDiscardRectangleStateCreateFlagBitsEXT {
-//}
-
-@extension("VK_EXT_conservative_rasterization") // 102
-type VkFlags VkPipelineRasterizationConservativeStateCreateFlagsEXT
-//@extension("VK_EXT_conservative_rasterization") // 102
-//bitfield VkPipelineRasterizationConservativeStateCreateFlagBitsEXT {
-//}
-
-@extension("VK_KHR_external_fence_capabilities") // 113
-type VkFlags VkExternalFenceHandleTypeFlagsKHR
-@extension("VK_KHR_external_fence_capabilities") // 113
-bitfield VkExternalFenceHandleTypeFlagBitsKHR {
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR         = 0x00000001,
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR      = 0x00000002,
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR  = 0x00000004,
-    VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR           = 0x00000008,
-}
-
-@extension("VK_KHR_external_fence_capabilities") // 113
-type VkFlags VkExternalFenceFeatureFlagsKHR
-@extension("VK_KHR_external_fence_capabilities") // 113
-bitfield VkExternalFenceFeatureFlagBitsKHR {
-    VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT_KHR            = 0x00000001,
-    VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT_KHR            = 0x00000002,
-}
-
-@extension("VK_KHR_external_fence") // 114
-type VkFlags VkFenceImportFlagsKHR
-@extension("VK_KHR_external_fence") // 114
-bitfield VkFenceImportFlagBitsKHR {
-    VK_FENCE_IMPORT_TEMPORARY_BIT_KHR                       = 0x00000001,
-}
-
-@extension("VK_MVK_ios_surface") // 123
-type VkFlags VkIOSSurfaceCreateFlagsMVK
-//@extension("VK_MVK_ios_surface") // 123
-//bitfield VkIOSSurfaceCreateFlagBitsMVK {
-//}
-
-@extension("VK_MVK_macos_surface") // 124
-type VkFlags VkMacOSSurfaceCreateFlagsMVK
-//@extension("VK_MVK_macos_surface") // 124
-//bitfield VkMacOSSurfaceCreateFlagBitsMVK {
-//}
-
-@extension("VK_EXT_debug_utils") // 129
-type VkFlags VkDebugUtilsMessengerCallbackDataFlagsEXT
-//@extension("VK_EXT_debug_utils") // 129
-//bitfield VkDebugUtilsMessengerCallbackDataFlagBitsEXT {
-//}
-
-@extension("VK_EXT_debug_utils") // 129
-type VkFlags VkDebugUtilsMessengerCreateFlagsEXT
-//@extension("VK_EXT_debug_utils") // 129
-//bitfield VkDebugUtilsMessengerCreateFlagBitsEXT {
-//}
-
-@extension("VK_EXT_debug_utils") // 129
-type VkFlags VkDebugUtilsMessageSeverityFlagsEXT
-@extension("VK_EXT_debug_utils") // 129
-bitfield VkDebugUtilsMessageSeverityFlagBitsEXT {
-    VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 0x00000001,
-    VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT    = 0x00000010,
-    VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 0x00000100,
-    VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT   = 0x00001000,
-}
-
-@extension("VK_EXT_debug_utils") // 129
-type VkFlags VkDebugUtilsMessageTypeFlagsEXT
-@extension("VK_EXT_debug_utils") // 129
-bitfield VkDebugUtilsMessageTypeFlagBitsEXT {
-    VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT     = 0x00000001,
-    VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT  = 0x00000002,
-    VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 0x00000004,
-}
-
-@extension("VK_NV_fragment_coverage_to_color") // 150
-type VkFlags VkPipelineCoverageToColorStateCreateFlagsNV
-@extension("VK_NV_fragment_coverage_to_color") // 150
-//bitfield VkPipelineCoverageToColorStateCreateFlagBitsNV {
-//}
-
-@extension("VK_NV_framebuffer_mixed_samples") // 153
-type VkFlags VkPipelineCoverageModulationStateCreateFlagsNV
-@extension("VK_NV_framebuffer_mixed_samples") // 153
-//bitfield VkPipelineCoverageModulationStateCreateFlagBitsNV {
-//}
-
-@extension("VK_EXT_validation_cache") // 161
-type VkFlags VkValidationCacheCreateFlagsEXT
-@extension("VK_EXT_validation_cache") // 161
-//bitfield VkValidationCacheCreateFlagBitsEXT {
-//}
-
-@extension("VK_EXT_descriptor_indexing") // 162
-type VkFlags VkDescriptorBindingFlagsEXT
-@extension("VK_EXT_descriptor_indexing") // 162
-bitfield VkDescriptorBindingFlagBitsEXT {
-    VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT             = 0x00000001,
-    VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT   = 0x00000002,
-    VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT               = 0x00000004,
-    VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT     = 0x00000008,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-type VkFlags VkGeometryFlagsNV
-@extension("VK_NV_ray_tracing") // 166
-bitfield VkGeometryFlagBitsNV {
-    VK_GEOMETRY_OPAQUE_BIT_NV                           = 0x00000001,
-    VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_NV  = 0x00000002,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-type VkFlags VkGeometryInstanceFlagsNV
-@extension("VK_NV_ray_tracing") // 166
-bitfield VkGeometryInstanceFlagBitsNV {
-    VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV           = 0x00000001,
-    VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_NV = 0x00000002,
-    VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_NV                    = 0x00000004,
-    VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_NV                 = 0x00000008,
-}
-
-@extension("VK_NV_ray_tracing") // 166
-type VkFlags VkBuildAccelerationStructureFlagsNV
-@extension("VK_NV_ray_tracing") // 166
-bitfield VkBuildAccelerationStructureFlagBitsNV {
-    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV         = 0x00000001,
-    VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV     = 0x00000002,
-    VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_NV    = 0x00000004,
-    VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_NV    = 0x00000008,
-    VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_NV           = 0x00000010,
-}
-
-@extension("VK_FUCHSIA_imagepipe_surface") // 215
-type VkFlags VkImagePipeSurfaceCreateFlagsFUCHSIA
-//@extension("VK_FUCHSIA_imagepipe_surface") // 215
-//bitfield VkImagePipeSurfaceCreateFlagBitsFUCHSIA {
-//}
-
-//////////////////
-//  Structures  //
-//////////////////
-
-class VkOffset2D {
-    s32                                         x
-    s32                                         y
-}
-
-class VkOffset3D {
-    s32                                         x
-    s32                                         y
-    s32                                         z
-}
-
-class VkExtent2D {
-    u32                                         width
-    u32                                         height
-}
-
-class VkExtent3D {
-    u32                                         width
-    u32                                         height
-    u32                                         depth
-}
-
-class VkViewport {
-    f32                                         x
-    f32                                         y
-    f32                                         width
-    f32                                         height
-    f32                                         minDepth
-    f32                                         maxDepth
-}
-
-class VkRect2D {
-    VkOffset2D                                  offset
-    VkExtent2D                                  extent
-}
-
-class VkClearRect {
-    VkRect2D                                    rect
-    u32                                         baseArrayLayer
-    u32                                         layerCount
-}
-
-class VkComponentMapping {
-    VkComponentSwizzle                          r
-    VkComponentSwizzle                          g
-    VkComponentSwizzle                          b
-    VkComponentSwizzle                          a
-}
-
-class VkPhysicalDeviceProperties {
-    u32                                         apiVersion
-    u32                                         driverVersion
-    u32                                         vendorID
-    u32                                         deviceID
-    VkPhysicalDeviceType                        deviceType
-    char[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]      deviceName
-    u8[VK_UUID_SIZE]                            pipelineCacheUUID
-    VkPhysicalDeviceLimits                      limits
-    VkPhysicalDeviceSparseProperties            sparseProperties
-}
-
-class VkExtensionProperties {
-    char[VK_MAX_EXTENSION_NAME_SIZE]            extensionName      /// extension name
-    u32                                         specVersion        /// version of the extension specification implemented
-}
-
-class VkLayerProperties {
-    char[VK_MAX_EXTENSION_NAME_SIZE]            layerName               /// layer name
-    u32                                         specVersion             /// version of the layer specification implemented
-    u32                                         implementationVersion   /// build or release version of the layer's library
-    char[VK_MAX_DESCRIPTION_SIZE]               description             /// Free-form description of the layer
-}
-
-class VkSubmitInfo {
-    VkStructureType                             sType              /// Type of structure. Should be VK_STRUCTURE_TYPE_SUBMIT_INFO
-    const void*                                 pNext              /// Next structure in chain
-    u32                                         waitSemaphoreCount
-    const VkSemaphore*                          pWaitSemaphores
-    const VkPipelineStageFlags*                 pWaitDstStageMask
-    u32                                         commandBufferCount
-    const VkCommandBuffer*                      pCommandBuffers
-    u32                                         signalSemaphoreCount
-    const VkSemaphore*                          pSignalSemaphores
-}
-
-class VkApplicationInfo {
-    VkStructureType                             sType              /// Type of structure. Should be VK_STRUCTURE_TYPE_APPLICATION_INFO
-    const void*                                 pNext              /// Next structure in chain
-    const char*                                 pApplicationName
-    u32                                         applicationVersion
-    const char*                                 pEngineName
-    u32                                         engineVersion
-    u32                                         apiVersion
-}
-
-class VkAllocationCallbacks {
-    void*                                       pUserData
-    PFN_vkAllocationFunction                    pfnAllocation
-    PFN_vkReallocationFunction                  pfnReallocation
-    PFN_vkFreeFunction                          pfnFree
-    PFN_vkInternalAllocationNotification        pfnInternalAllocation
-    PFN_vkInternalFreeNotification              pfnInternalFree
-}
-
-class VkDeviceQueueCreateInfo {
-    VkStructureType                             sStype                    /// Should be VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO
-    const void*                                 pNext                     /// Pointer to next structure
-    VkDeviceQueueCreateFlags                    flags
-    u32                                         queueFamilyIndex
-    u32                                         queueCount
-    const f32*                                  pQueuePriorities
-}
-
-class VkDeviceCreateInfo {
-    VkStructureType                             sType                      /// Should be VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
-    const void*                                 pNext                      /// Pointer to next structure
-    VkDeviceCreateFlags                         flags
-    u32                                         queueCreateInfoCount
-    const VkDeviceQueueCreateInfo*              pQueueCreateInfos
-    u32                                         enabledLayerCount
-    const char* const*                          ppEnabledLayerNames        /// Ordered list of layer names to be enabled
-    u32                                         enabledExtensionCount
-    const char* const*                          ppEnabledExtensionNames
-    const VkPhysicalDeviceFeatures*             pEnabledFeatures
-}
-
-class VkInstanceCreateInfo {
-    VkStructureType                             sType                      /// Should be VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
-    const void*                                 pNext                      /// Pointer to next structure
-    VkInstanceCreateFlags                       flags
-    const VkApplicationInfo*                    pApplicationInfo
-    u32                                         enabledLayerCount
-    const char* const*                          ppEnabledLayerNames        /// Ordered list of layer names to be enabled
-    u32                                         enabledExtensionCount
-    const char* const*                          ppEnabledExtensionNames    /// Extension names to be enabled
-}
-
-class VkQueueFamilyProperties {
-    VkQueueFlags                                queueFlags                 /// Queue flags
-    u32                                         queueCount
-    u32                                         timestampValidBits
-    VkExtent3D                                  minImageTransferGranularity
-}
-
-class VkPhysicalDeviceMemoryProperties {
-    u32                                         memoryTypeCount
-    VkMemoryType[VK_MAX_MEMORY_TYPES]           memoryTypes
-    u32                                         memoryHeapCount
-    VkMemoryHeap[VK_MAX_MEMORY_HEAPS]           memoryHeaps
-}
-
-class VkMemoryAllocateInfo {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
-    const void*                                 pNext                      /// Pointer to next structure
-    VkDeviceSize                                allocationSize             /// Size of memory allocation
-    u32                                         memoryTypeIndex            /// Index of the of the memory type to allocate from
-}
-
-class VkMemoryRequirements {
-    VkDeviceSize                                size                       /// Specified in bytes
-    VkDeviceSize                                alignment                  /// Specified in bytes
-    u32                                         memoryTypeBits             /// Bitfield of the allowed memory type indices into memoryTypes[] for this object
-}
-
-class VkSparseImageFormatProperties {
-    VkImageAspectFlagBits                       aspectMask
-    VkExtent3D                                  imageGranularity
-    VkSparseImageFormatFlags                    flags
-}
-
-class VkSparseImageMemoryRequirements {
-    VkSparseImageFormatProperties               formatProperties
-    u32                                         imageMipTailFirstLod
-    VkDeviceSize                                imageMipTailSize           /// Specified in bytes, must be a multiple of image block size / alignment
-    VkDeviceSize                                imageMipTailOffset         /// Specified in bytes, must be a multiple of image block size / alignment
-    VkDeviceSize                                imageMipTailStride         /// Specified in bytes, must be a multiple of image block size / alignment
-}
-
-class VkMemoryType {
-    VkMemoryPropertyFlags                       propertyFlags              /// Memory properties of this memory type
-    u32                                         heapIndex                  /// Index of the memory heap allocations of this memory type are taken from
-}
-
-class VkMemoryHeap {
-    VkDeviceSize                                size                       /// Available memory in the heap
-    VkMemoryHeapFlags                           flags                      /// Flags for the heap
-}
-
-class VkMappedMemoryRange {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE
-    const void*                                 pNext                      /// Pointer to next structure
-    VkDeviceMemory                              memory                     /// Mapped memory object
-    VkDeviceSize                                offset                     /// Offset within the mapped memory the range starts from
-    VkDeviceSize                                size                       /// Size of the range within the mapped memory
-}
-
-class VkFormatProperties {
-    VkFormatFeatureFlags                        linearTilingFeatures       /// Format features in case of linear tiling
-    VkFormatFeatureFlags                        optimalTilingFeatures      /// Format features in case of optimal tiling
-    VkFormatFeatureFlags                        bufferFeatures             /// Format features supported by buffers
-}
-
-class VkImageFormatProperties {
-    VkExtent3D                                  maxExtent                  /// max image dimensions for this resource type
-    u32                                         maxMipLevels               /// max number of mipmap levels for this resource type
-    u32                                         maxArrayLayers             /// max array layers for this resource type
-    VkSampleCountFlags                          sampleCounts               /// supported sample counts for this resource type
-    VkDeviceSize                                maxResourceSize            /// max size (in bytes) of this resource type
-}
-
-class VkDescriptorImageInfo {
-    VkSampler                                   sampler
-    VkImageView                                 imageView
-    VkImageLayout                               imageLayout
-}
-
-class VkDescriptorBufferInfo {
-    VkBuffer                                    buffer                     /// Buffer used for this descriptor when the descriptor is UNIFORM_BUFFER[_DYNAMIC]
-    VkDeviceSize                                offset                     /// Base offset from buffer start in bytes to update in the descriptor set.
-    VkDeviceSize                                range                      /// Size in bytes of the buffer resource for this descriptor update.
-}
-
-class VkWriteDescriptorSet {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET
-    const void*                                 pNext                      /// Pointer to next structure
-    VkDescriptorSet                             dstSet                     /// Destination descriptor set
-    u32                                         dstBinding                 /// Binding within the destination descriptor set to write
-    u32                                         dstArrayElement            /// Array element within the destination binding to write
-    u32                                         descriptorCount            /// Number of descriptors to write (determines the size of the array pointed by <pDescriptors>)
-    VkDescriptorType                            descriptorType             /// Descriptor type to write (determines which fields of the array pointed by <pDescriptors> are going to be used)
-    const VkDescriptorImageInfo*                pImageInfo
-    const VkDescriptorBufferInfo*               pBufferInfo
-    const VkBufferView*                         pTexelBufferView
-}
-
-class VkCopyDescriptorSet {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET
-    const void*                                 pNext                      /// Pointer to next structure
-    VkDescriptorSet                             srcSet                     /// Source descriptor set
-    u32                                         srcBinding                 /// Binding within the source descriptor set to copy from
-    u32                                         srcArrayElement            /// Array element within the source binding to copy from
-    VkDescriptorSet                             dstSet                     /// Destination descriptor set
-    u32                                         dstBinding                 /// Binding within the destination descriptor set to copy to
-    u32                                         dstArrayElement            /// Array element within the destination binding to copy to
-    u32                                         descriptorCount            /// Number of descriptors to copy
-}
-
-class VkBufferCreateInfo {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
-    const void*                                 pNext                      /// Pointer to next structure.
-    VkBufferCreateFlags                         flags                      /// Buffer creation flags
-    VkDeviceSize                                size                       /// Specified in bytes
-    VkBufferUsageFlags                          usage                      /// Buffer usage flags
-    VkSharingMode                               sharingMode
-    u32                                         queueFamilyIndexCount
-    const u32*                                  pQueueFamilyIndices
-}
-
-class VkBufferViewCreateInfo {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO
-    const void*                                 pNext                      /// Pointer to next structure.
-    VkBufferViewCreateFlags                     flags
-    VkBuffer                                    buffer
-    VkFormat                                    format                     /// Optionally specifies format of elements
-    VkDeviceSize                                offset                     /// Specified in bytes
-    VkDeviceSize                                range                      /// View size specified in bytes
-}
-
-class VkImageSubresource {
-    VkImageAspectFlagBits                       aspectMask
-    u32                                         mipLevel
-    u32                                         arrayLayer
-}
-
-class VkImageSubresourceRange {
-    VkImageAspectFlags                          aspectMask
-    u32                                         baseMipLevel
-    u32                                         levelCount
-    u32                                         baseArrayLayer
-    u32                                         layerCount
-}
-
-class VkMemoryBarrier {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_MEMORY_BARRIER
-    const void*                                 pNext                      /// Pointer to next structure.
-    VkAccessFlags                               srcAccessMask
-    VkAccessFlags                               dstAccessMask
-}
-
-class VkBufferMemoryBarrier {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER
-    const void*                                 pNext                      /// Pointer to next structure.
-    VkAccessFlags                               srcAccessMask
-    VkAccessFlags                               dstAccessMask
-    u32                                         srcQueueFamilyIndex        /// Queue family to transition ownership from
-    u32                                         dstQueueFamilyIndex        /// Queue family to transition ownership to
-    VkBuffer                                    buffer                     /// Buffer to sync
-    VkDeviceSize                                offset                     /// Offset within the buffer to sync
-    VkDeviceSize                                size                       /// Amount of bytes to sync
-}
-
-class VkImageMemoryBarrier {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER
-    const void*                                 pNext                      /// Pointer to next structure.
-    VkAccessFlags                               srcAccessMask
-    VkAccessFlags                               dstAccessMask
-    VkImageLayout                               oldLayout                  /// Current layout of the image
-    VkImageLayout                               newLayout                  /// New layout to transition the image to
-    u32                                         srcQueueFamilyIndex        /// Queue family to transition ownership from
-    u32                                         dstQueueFamilyIndex        /// Queue family to transition ownership to
-    VkImage                                     image                      /// Image to sync
-    VkImageSubresourceRange                     subresourceRange           /// Subresource range to sync
-}
-
-class VkImageCreateInfo {
-    VkStructureType                             sType                      /// Must be VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
-    const void*                                 pNext                      /// Pointer to next structure.
-    VkImageCreateFlags                          flags                      /// Image creation flags
-    VkImageType                                 imageType
-    VkFormat                                    format
-    VkExtent3D                                  extent
-    u32                                         mipLevels
-    u32                                         arrayLayers
-    VkSampleCountFlagBits                       samples
-    VkImageTiling                               tiling
-    VkImageUsageFlags                           usage                      /// Image usage flags
-    VkSharingMode                               sharingMode                /// Cross-queue-family sharing mode
-    u32                                         queueFamilyIndexCount      /// Number of queue families to share across
-    const u32*                                  pQueueFamilyIndices        /// Array of queue family indices to share across
-    VkImageLayout                               initialLayout              /// Initial image layout for all subresources
-}
-
-class VkSubresourceLayout {
-    VkDeviceSize                                offset                 /// Specified in bytes
-    VkDeviceSize                                size                   /// Specified in bytes
-    VkDeviceSize                                rowPitch               /// Specified in bytes
-    VkDeviceSize                                arrayPitch             /// Specified in bytes
-    VkDeviceSize                                depthPitch             /// Specified in bytes
-}
-
-class VkImageViewCreateInfo {
-    VkStructureType                             sType                  /// Must be VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO
-    const void*                                 pNext                  /// Pointer to next structure
-    VkImageViewCreateFlags                      flags
-    VkImage                                     image
-    VkImageViewType                             viewType
-    VkFormat                                    format
-    VkComponentMapping                          components
-    VkImageSubresourceRange                     subresourceRange
-}
-
-class VkBufferCopy {
-    VkDeviceSize                                srcOffset              /// Specified in bytes
-    VkDeviceSize                                dstOffset              /// Specified in bytes
-    VkDeviceSize                                size                   /// Specified in bytes
-}
-
-class VkSparseMemoryBind {
-    VkDeviceSize                                resourceOffset        /// Specified in bytes
-    VkDeviceSize                                size                  /// Specified in bytes
-    VkDeviceMemory                              memory
-    VkDeviceSize                                memoryOffset          /// Specified in bytes
-    VkSparseMemoryBindFlags                     flags
-}
-
-class VkSparseImageMemoryBind {
-    VkImageSubresource                          subresource
-    VkOffset3D                                  offset
-    VkExtent3D                                  extent
-    VkDeviceMemory                              memory
-    VkDeviceSize                                memoryOffset          /// Specified in bytes
-    VkSparseMemoryBindFlags                     flags
-}
-
-class VkSparseBufferMemoryBindInfo {
-    VkBuffer                                    buffer
-    u32                                         bindCount
-    const VkSparseMemoryBind*                   pBinds
-}
-
-class VkSparseImageOpaqueMemoryBindInfo {
-    VkImage                                     image
-    u32                                         bindCount
-    const VkSparseMemoryBind*                   pBinds
-}
-
-class VkSparseImageMemoryBindInfo {
-    VkImage                                     image
-    u32                                         bindCount
-    const VkSparseMemoryBind*                   pBinds
-}
-
-class VkBindSparseInfo {
-    VkStructureType                             sType                 /// Must be VK_STRUCTURE_TYPE_BIND_SPARSE_INFO
-    const void*                                 pNext
-    u32                                         waitSemaphoreCount
-    const VkSemaphore*                          pWaitSemaphores
-    u32                                         numBufferBinds
-    const VkSparseBufferMemoryBindInfo*         pBufferBinds
-    u32                                         numImageOpaqueBinds
-    const VkSparseImageOpaqueMemoryBindInfo*    pImageOpaqueBinds
-    u32                                         numImageBinds
-    const VkSparseImageMemoryBindInfo*          pImageBinds
-    u32                                         signalSemaphoreCount
-    const VkSemaphore*                          pSignalSemaphores
-}
-
-class VkImageSubresourceLayers {
-    VkImageAspectFlags                          aspectMask
-    u32                                         mipLevel
-    u32                                         baseArrayLayer
-    u32                                         layerCount
-}
-
-class VkImageCopy {
-    VkImageSubresourceLayers                    srcSubresource
-    VkOffset3D                                  srcOffset             /// Specified in pixels for both compressed and uncompressed images
-    VkImageSubresourceLayers                    dstSubresource
-    VkOffset3D                                  dstOffset             /// Specified in pixels for both compressed and uncompressed images
-    VkExtent3D                                  extent                /// Specified in pixels for both compressed and uncompressed images
-}
-
-class VkImageBlit {
-    VkImageSubresourceLayers                    srcSubresource
-    VkOffset3D[2]                               srcOffsets
-    VkImageSubresourceLayers                    dstSubresource
-    VkOffset3D[2]                               dstOffsets
-}
-
-class VkBufferImageCopy {
-    VkDeviceSize                                bufferOffset           /// Specified in bytes
-    u32                                         bufferRowLength        /// Specified in texels
-    u32                                         bufferImageHeight
-    VkImageSubresourceLayers                    imageSubresource
-    VkOffset3D                                  imageOffset            /// Specified in pixels for both compressed and uncompressed images
-    VkExtent3D                                  imageExtent            /// Specified in pixels for both compressed and uncompressed images
-}
-
-class VkImageResolve {
-    VkImageSubresourceLayers                    srcSubresource
-    VkOffset3D                                  srcOffset
-    VkImageSubresourceLayers                    dstSubresource
-    VkOffset3D                                  dstOffset
-    VkExtent3D                                  extent
-}
-
-class VkShaderModuleCreateInfo {
-    VkStructureType                             sType                  /// Must be VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
-    const void*                                 pNext                  /// Pointer to next structure
-    VkShaderModuleCreateFlags                   flags                  /// Reserved
-    platform.size_t                             codeSize               /// Specified in bytes
-    const u32*                                  pCode                  /// Binary code of size codeSize
-}
-
-class VkDescriptorSetLayoutBinding {
-    u32                                         binding
-    VkDescriptorType                            descriptorType     /// Type of the descriptors in this binding
-    u32                                         descriptorCount    /// Number of descriptors in this binding
-    VkShaderStageFlags                          stageFlags         /// Shader stages this binding is visible to
-    const VkSampler*                            pImmutableSamplers /// Immutable samplers (used if descriptor type is SAMPLER or COMBINED_IMAGE_SAMPLER, is either NULL or contains <count> number of elements)
-}
-
-class VkDescriptorSetLayoutCreateInfo {
-    VkStructureType                             sType              /// Must be VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO
-    const void*                                 pNext              /// Pointer to next structure
-    VkDescriptorSetLayoutCreateFlags            flags
-    u32                                         bindingCount       /// Number of bindings in the descriptor set layout
-    const VkDescriptorSetLayoutBinding*         pBindings          /// Array of descriptor set layout bindings
-}
-
-class VkDescriptorPoolSize {
-    VkDescriptorType                            type
-    u32                                         descriptorCount
-}
-
-class VkDescriptorPoolCreateInfo {
-    VkStructureType                             sType              /// Must be VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO
-    const void*                                 pNext              /// Pointer to next structure
-    VkDescriptorPoolCreateFlags                 flags
-    u32                                         maxSets
-    u32                                         poolSizeCount
-    const VkDescriptorPoolSize*                 pPoolSizes
-}
-
-class VkDescriptorSetAllocateInfo {
-    VkStructureType                             sType              /// Must be VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO
-    const void*                                 pNext              /// Pointer to next structure
-    VkDescriptorPool                            descriptorPool
-    u32                                         setCount
-    const VkDescriptorSetLayout*                pSetLayouts
-}
-
-class VkSpecializationMapEntry {
-    u32                                         constantID         /// The SpecConstant ID specified in the BIL
-    u32                                         offset             /// Offset of the value in the data block
-    platform.size_t                             size               /// Size in bytes of the SpecConstant
-}
-
-class VkSpecializationInfo {
-    u32                                         mapEntryCount      /// Number of entries in the map
-    const VkSpecializationMapEntry*             pMapEntries        /// Array of map entries
-    platform.size_t                             dataSize           /// Size in bytes of pData
-    const void*                                 pData              /// Pointer to SpecConstant data
-}
-
-class VkPipelineShaderStageCreateInfo {
-    VkStructureType                             sType              /// Must be VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO
-    const void*                                 pNext              /// Pointer to next structure
-    VkPipelineShaderStageCreateFlags            flags
-    VkShaderStageFlagBits                       stage
-    VkShaderModule                              module
-    const char*                                 pName
-    const VkSpecializationInfo*                 pSpecializationInfo
-}
-
-class VkComputePipelineCreateInfo {
-    VkStructureType                             sType              /// Must be VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO
-    const void*                                 pNext              /// Pointer to next structure
-    VkPipelineCreateFlags                       flags              /// Pipeline creation flags
-    VkPipelineShaderStageCreateInfo             stage
-    VkPipelineLayout                            layout             /// Interface layout of the pipeline
-    VkPipeline                                  basePipelineHandle /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of
-    s32                                         basePipelineIndex  /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is not -1, it specifies an index into pCreateInfos of the base pipeline this is a derivative of
-}
-
-class VkVertexInputBindingDescription {
-    u32                                         binding               /// Vertex buffer binding id
-    u32                                         stride                /// Distance between vertices in bytes (0 = no advancement)
-    VkVertexInputRate                           inputRate             /// Rate at which binding is incremented
-}
-
-class VkVertexInputAttributeDescription {
-    u32                                         location              /// location of the shader vertex attrib
-    u32                                         binding               /// Vertex buffer binding id
-    VkFormat                                    format                /// format of source data
-    u32                                         offset                /// Offset of first element in bytes from base of vertex
-}
-
-class VkPipelineVertexInputStateCreateInfo {
-    VkStructureType                             sType                           /// Should be VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
-    const void*                                 pNext                           /// Pointer to next structure
-    VkPipelineVertexInputStateCreateFlags       flags
-    u32                                         vertexBindingDescriptionCount   /// number of bindings
-    const VkVertexInputBindingDescription*      pVertexBindingDescriptions
-    u32                                         vertexAttributeDescriptionCount /// number of attributes
-    const VkVertexInputAttributeDescription*    pVertexAttributeDescriptions
-}
-
-class VkPipelineInputAssemblyStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineInputAssemblyStateCreateFlags     flags
-    VkPrimitiveTopology                         topology
-    VkBool32                                    primitiveRestartEnable
-}
-
-class VkPipelineTessellationStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineTessellationStateCreateFlags      flags
-    u32                                         patchControlPoints
-}
-
-class VkPipelineViewportStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineViewportStateCreateFlags          flags
-    u32                                         viewportCount
-    const VkViewport*                           pViewports
-    u32                                         scissorCount
-    const VkRect2D*                             pScissors
-}
-
-class VkPipelineRasterizationStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineRasterizationStateCreateFlags     flags
-    VkBool32                                    depthClampEnable
-    VkBool32                                    rasterizerDiscardEnable
-    VkPolygonMode                               polygonMode                   /// optional (GL45)
-    VkCullModeFlags                             cullMode
-    VkFrontFace                                 frontFace
-    VkBool32                                    depthBiasEnable
-    f32                                         depthBiasConstantFactor
-    f32                                         depthBiasClamp
-    f32                                         depthBiasSlopeFactor
-    f32                                         lineWidth
-}
-
-class VkPipelineMultisampleStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineMultisampleStateCreateFlags       flags
-    VkSampleCountFlagBits                       rasterizationSamples       /// Number of samples used for rasterization
-    VkBool32                                    sampleShadingEnable        /// optional (GL45)
-    f32                                         minSampleShading           /// optional (GL45)
-    const VkSampleMask*                         pSampleMask
-    VkBool32                                    alphaToCoverageEnable
-    VkBool32                                    alphaToOneEnable
-}
-
-class VkPipelineColorBlendAttachmentState {
-    VkBool32                                    blendEnable
-    VkBlendFactor                               srcColorBlendFactor
-    VkBlendFactor                               dstColorBlendFactor
-    VkBlendOp                                   colorBlendOp
-    VkBlendFactor                               srcAlphaBlendFactor
-    VkBlendFactor                               dstAlphaBlendFactor
-    VkBlendOp                                   alphaBlendOp
-    VkColorComponentFlags                       colorWriteMask
-}
-
-class VkPipelineColorBlendStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineColorBlendStateCreateFlags        flags
-    VkBool32                                    logicOpEnable
-    VkLogicOp                                   logicOp
-    u32                                         attachmentCount    /// # of pAttachments
-    const VkPipelineColorBlendAttachmentState*  pAttachments
-    f32[4]                                      blendConstants
-}
-
-class VkStencilOpState {
-    VkStencilOp                                 failOp
-    VkStencilOp                                 passOp
-    VkStencilOp                                 depthFailOp
-    VkCompareOp                                 compareOp
-    u32                                         compareMask
-    u32                                         writeMask
-    u32                                         reference
-}
-
-class VkPipelineDepthStencilStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineDepthStencilStateCreateFlags      flags
-    VkBool32                                    depthTestEnable
-    VkBool32                                    depthWriteEnable
-    VkCompareOp                                 depthCompareOp
-    VkBool32                                    depthBoundsTestEnable  /// optional (depth_bounds_test)
-    VkBool32                                    stencilTestEnable
-    VkStencilOpState                            front
-    VkStencilOpState                            back
-    f32                                         minDepthBounds
-    f32                                         maxDepthBounds
-}
-
-class VkPipelineDynamicStateCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkPipelineDynamicStateCreateFlags           flags
-    u32                                         dynamicStateCount
-    const VkDynamicState*                       pDynamicStates
-}
-
-class VkGraphicsPipelineCreateInfo {
-    VkStructureType                                 sType               /// Must be VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO
-    const void*                                     pNext               /// Pointer to next structure
-    VkPipelineCreateFlags                           flags               /// Pipeline creation flags
-    u32                                             stageCount
-    const VkPipelineShaderStageCreateInfo*          pStages             /// One entry for each active shader stage
-    const VkPipelineVertexInputStateCreateInfo*     pVertexInputState
-    const VkPipelineInputAssemblyStateCreateInfo*   pInputAssemblyState
-    const VkPipelineTessellationStateCreateInfo*    pTessellationState
-    const VkPipelineViewportStateCreateInfo*        pViewportState
-    const VkPipelineRasterizationStateCreateInfo*   pRasterizationState
-    const VkPipelineMultisampleStateCreateInfo*     pMultisampleState
-    const VkPipelineDepthStencilStateCreateInfo*    pDepthStencilState
-    const VkPipelineColorBlendStateCreateInfo*      pColorBlendState
-    const VkPipelineDynamicStateCreateInfo*         pDynamicState
-    VkPipelineLayout                                layout              /// Interface layout of the pipeline
-    VkRenderPass                                    renderPass
-    u32                                             subpass
-    VkPipeline                                      basePipelineHandle  /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of
-    s32                                             basePipelineIndex   /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is not -1, it specifies an index into pCreateInfos of the base pipeline this is a derivative of
-}
-
-class VkPipelineCacheCreateInfo {
-    VkStructureType                             sType                 /// Must be VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO
-    const void*                                 pNext                 /// Pointer to next structure
-    VkPipelineCacheCreateFlags                  flags
-    platform.size_t                             initialDataSize       /// Size of initial data to populate cache, in bytes
-    const void*                                 pInitialData          /// Initial data to populate cache
-}
-
-class VkPushConstantRange {
-    VkShaderStageFlags                          stageFlags   /// Which stages use the range
-    u32                                         offset       /// Start of the range, in bytes
-    u32                                         size        /// Length of the range, in bytes
-}
-
-class VkPipelineLayoutCreateInfo {
-    VkStructureType                             sType                   /// Must be VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO
-    const void*                                 pNext                   /// Pointer to next structure
-    VkPipelineLayoutCreateFlags                 flags
-    u32                                         descriptorSetCount      /// Number of descriptor sets interfaced by the pipeline
-    const VkDescriptorSetLayout*                pSetLayouts             /// Array of <setCount> number of descriptor set layout objects defining the layout of the
-    u32                                         pushConstantRangeCount  /// Number of push-constant ranges used by the pipeline
-    const VkPushConstantRange*                  pPushConstantRanges     /// Array of pushConstantRangeCount number of ranges used by various shader stages
-}
-
-class VkSamplerCreateInfo {
-    VkStructureType                             sType          /// Must be VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO
-    const void*                                 pNext          /// Pointer to next structure
-    VkSamplerCreateFlags                        flags
-    VkFilter                                    magFilter      /// Filter mode for magnification
-    VkFilter                                    minFilter      /// Filter mode for minifiation
-    VkSamplerMipmapMode                         mipmapMode     /// Mipmap selection mode
-    VkSamplerAddressMode                        addressModeU
-    VkSamplerAddressMode                        addressModeV
-    VkSamplerAddressMode                        addressModeW
-    f32                                         mipLodBias
-    VkBool32                                    anisotropyEnable
-    f32                                         maxAnisotropy
-    VkBool32                                    compareEnable
-    VkCompareOp                                 compareOp
-    f32                                         minLod
-    f32                                         maxLod
-    VkBorderColor                               borderColor
-    VkBool32                                    unnormalizedCoordinates
-}
-
-class VkCommandPoolCreateInfo {
-    VkStructureType                             sType            /// Must be VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO
-    const void*                                 pNext            /// Pointer to next structure
-    VkCommandPoolCreateFlags                    flags            /// Command pool creation flags
-    u32                                         queueFamilyIndex
-}
-
-class VkCommandBufferAllocateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkCommandPool                               commandPool
-    VkCommandBufferLevel                        level
-    u32                                         commandBufferCount
-}
-
-class VkCommandBufferInheritanceInfo {
-    VkStructureType                             sType       /// Must be VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO
-    const void*                                 pNext       /// Pointer to next structure
-    VkRenderPass                                renderPass  /// Render pass for secondary command buffers
-    u32                                         subpass
-    VkFramebuffer                               framebuffer /// Framebuffer for secondary command buffers
-    VkBool32                                    occlusionQueryEnable
-    VkQueryControlFlags                         queryFlags
-    VkQueryPipelineStatisticFlags               pipelineStatistics
-}
-
-class VkCommandBufferBeginInfo {
-    VkStructureType                             sType       /// Must be VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO
-    const void*                                 pNext       /// Pointer to next structure
-    VkCommandBufferUsageFlags                   flags       /// Command buffer usage flags
-    const VkCommandBufferInheritanceInfo*       pInheritanceInfo
-}
-
-class VkRenderPassBeginInfo {
-    VkStructureType                             sType       /// Must be VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO
-    const void*                                 pNext       /// Pointer to next structure
-    VkRenderPass                                renderPass
-    VkFramebuffer                               framebuffer
-    VkRect2D                                    renderArea
-    u32                                         clearValueCount
-    const VkClearValue*                         pClearValues
-}
-
-@union
-/// Union allowing specification of floating point, integer, or unsigned integer color data. Actual value selected is based on image/attachment being cleared.
-class VkClearColorValue {
-    f32[4]                                      float32
-    s32[4]                                      int32
-    u32[4]                                      uint32
-}
-
-class VkClearDepthStencilValue {
-    f32                                         depth
-    u32                                         stencil
-}
-
-@union
-/// Union allowing specification of color, depth, and stencil color values. Actual value selected is based on attachment being cleared.
-class VkClearValue {
-    VkClearColorValue                           color
-    VkClearDepthStencilValue                    depthStencil
-}
-
-class VkClearAttachment {
-    VkImageAspectFlags                          aspectMask
-    u32                                         colorAttachment
-    VkClearValue                                clearValue
-}
-
-class VkAttachmentDescription {
-    VkAttachmentDescriptionFlags                flags
-    VkFormat                                    format
-    VkSampleCountFlagBits                       samples
-    VkAttachmentLoadOp                          loadOp          /// Load op for color or depth data
-    VkAttachmentStoreOp                         storeOp         /// Store op for color or depth data
-    VkAttachmentLoadOp                          stencilLoadOp   /// Load op for stencil data
-    VkAttachmentStoreOp                         stencilStoreOp  /// Store op for stencil data
-    VkImageLayout                               initialLayout
-    VkImageLayout                               finalLayout
-}
-
-class VkAttachmentReference {
-    u32                                         attachment
-    VkImageLayout                               layout
-}
-
-class VkSubpassDescription {
-    VkSubpassDescriptionFlags                   flags
-    VkPipelineBindPoint                         pipelineBindPoint  /// Must be VK_PIPELINE_BIND_POINT_GRAPHICS for now
-    u32                                         inputAttachmentCount
-    const VkAttachmentReference*                pInputAttachments
-    u32                                         colorAttachmentCount
-    const VkAttachmentReference*                pColorAttachments
-    const VkAttachmentReference*                pResolveAttachments
-    const VkAttachmentReference*                pDepthStencilAttachment
-    u32                                         preserveAttachmentCount
-    const u32*                                  pPreserveAttachments
-}
-
-class VkSubpassDependency {
-    u32                                         srcSubpass
-    u32                                         dstSubpass
-    VkPipelineStageFlags                        srcStageMask
-    VkPipelineStageFlags                        dstStageMask
-    VkAccessFlags                               srcAccessMask
-    VkAccessFlags                               dstAccessMask
-    VkDependencyFlags                           dependencyFlags
-}
-
-class VkRenderPassCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkRenderPassCreateFlags                     flags
-    u32                                         attachmentCount
-    const VkAttachmentDescription*              pAttachments
-    u32                                         subpassCount
-    const VkSubpassDescription*                 pSubpasses
-    u32                                         dependencyCount
-    const VkSubpassDependency*                  pDependencies
-}
-
-class VkEventCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_EVENT_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkEventCreateFlags                          flags      /// Event creation flags
-}
-
-class VkFenceCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkFenceCreateFlags                          flags      /// Fence creation flags
-}
-
-class VkPhysicalDeviceFeatures {
-    VkBool32                                    robustBufferAccess                        /// out of bounds buffer accesses are well defined
-    VkBool32                                    fullDrawIndexUint32                       /// full 32-bit range of indices for indexed draw calls
-    VkBool32                                    imageCubeArray                            /// image views which are arrays of cube maps
-    VkBool32                                    independentBlend                          /// blending operations are controlled per-attachment
-    VkBool32                                    geometryShader                            /// geometry stage
-    VkBool32                                    tessellationShader                        /// tessellation control and evaluation stage
-    VkBool32                                    sampleRateShading                         /// per-sample shading and interpolation
-    VkBool32                                    dualSrcBlend                              /// blend operations which take two sources
-    VkBool32                                    logicOp                                   /// logic operations
-    VkBool32                                    multiDrawIndirect                         /// multi draw indirect
-    VkBool32                                    drawIndirectFirstInstance
-    VkBool32                                    depthClamp                                /// depth clamping
-    VkBool32                                    depthBiasClamp                            /// depth bias clamping
-    VkBool32                                    fillModeNonSolid                          /// point and wireframe fill modes
-    VkBool32                                    depthBounds                               /// depth bounds test
-    VkBool32                                    wideLines                                 /// lines with width greater than 1
-    VkBool32                                    largePoints                               /// points with size greater than 1
-    VkBool32                                    alphaToOne                                /// The fragment alpha channel can be forced to maximum representable alpha value
-    VkBool32                                    multiViewport
-    VkBool32                                    samplerAnisotropy
-    VkBool32                                    textureCompressionETC2                    /// ETC texture compression formats
-    VkBool32                                    textureCompressionASTC_LDR                /// ASTC LDR texture compression formats
-    VkBool32                                    textureCompressionBC                      /// BC1-7 texture compressed formats
-    VkBool32                                    occlusionQueryPrecise
-    VkBool32                                    pipelineStatisticsQuery                   /// pipeline statistics query
-    VkBool32                                    vertexPipelineStoresAndAtomics
-    VkBool32                                    fragmentStoresAndAtomics
-    VkBool32                                    shaderTessellationAndGeometryPointSize
-    VkBool32                                    shaderImageGatherExtended                 /// texture gather with run-time values and independent offsets
-    VkBool32                                    shaderStorageImageExtendedFormats         /// the extended set of formats can be used for storage images
-    VkBool32                                    shaderStorageImageMultisample             /// multisample images can be used for storage images
-    VkBool32                                    shaderStorageImageReadWithoutFormat
-    VkBool32                                    shaderStorageImageWriteWithoutFormat
-    VkBool32                                    shaderUniformBufferArrayDynamicIndexing   /// arrays of uniform buffers can be accessed with dynamically uniform indices
-    VkBool32                                    shaderSampledImageArrayDynamicIndexing    /// arrays of sampled images can be accessed with dynamically uniform indices
-    VkBool32                                    shaderStorageBufferArrayDynamicIndexing   /// arrays of storage buffers can be accessed with dynamically uniform indices
-    VkBool32                                    shaderStorageImageArrayDynamicIndexing    /// arrays of storage images can be accessed with dynamically uniform indices
-    VkBool32                                    shaderClipDistance                        /// clip distance in shaders
-    VkBool32                                    shaderCullDistance                        /// cull distance in shaders
-    VkBool32                                    shaderFloat64                             /// 64-bit floats (doubles) in shaders
-    VkBool32                                    shaderInt64                               /// 64-bit integers in shaders
-    VkBool32                                    shaderInt16                               /// 16-bit integers in shaders
-    VkBool32                                    shaderResourceResidency                   /// shader can use texture operations that return resource residency information (requires sparseNonResident support)
-    VkBool32                                    shaderResourceMinLod                      /// shader can use texture operations that specify minimum resource LOD
-    VkBool32                                    sparseBinding                             /// Sparse resources support: Resource memory can be managed at opaque page level rather than object level
-    VkBool32                                    sparseResidencyBuffer                     /// Sparse resources support: GPU can access partially resident buffers
-    VkBool32                                    sparseResidencyImage2D                    /// Sparse resources support: GPU can access partially resident 2D (non-MSAA non-DepthStencil) images
-    VkBool32                                    sparseResidencyImage3D                    /// Sparse resources support: GPU can access partially resident 3D images
-    VkBool32                                    sparseResidency2Samples                   /// Sparse resources support: GPU can access partially resident MSAA 2D images with 2 samples
-    VkBool32                                    sparseResidency4Samples                   /// Sparse resources support: GPU can access partially resident MSAA 2D images with 4 samples
-    VkBool32                                    sparseResidency8Samples                   /// Sparse resources support: GPU can access partially resident MSAA 2D images with 8 samples
-    VkBool32                                    sparseResidency16Samples                  /// Sparse resources support: GPU can access partially resident MSAA 2D images with 16 samples
-    VkBool32                                    sparseResidencyAliased                    /// Sparse resources support: GPU can correctly access data aliased into multiple locations (opt-in)
-    VkBool32                                    variableMultisampleRate
-    VkBool32                                    inheritedQueries
-}
-
-class VkPhysicalDeviceLimits {
-    /// resource maximum sizes
-    u32                                         maxImageDimension1D                       /// max 1D image dimension
-    u32                                         maxImageDimension2D                       /// max 2D image dimension
-    u32                                         maxImageDimension3D                       /// max 3D image dimension
-    u32                                         maxImageDimensionCube                     /// max cubemap image dimension
-    u32                                         maxImageArrayLayers                       /// max layers for image arrays
-    u32                                         maxTexelBufferElements
-    u32                                         maxUniformBufferRange                     /// max uniform buffer size (bytes)
-    u32                                         maxStorageBufferRange                     /// max storage buffer size (bytes)
-    u32                                         maxPushConstantsSize                      /// max size of the push constants pool (bytes)
-    /// memory limits
-    u32                                         maxMemoryAllocationCount                  /// max number of device memory allocations supported
-    u32                                         maxSamplerAllocationCount
-    VkDeviceSize                                bufferImageGranularity                    /// Granularity (in bytes) at which buffers and images can be bound to adjacent memory for simultaneous usage
-    VkDeviceSize                                sparseAddressSpaceSize                    /// Total address space available for sparse allocations (bytes)
-    /// descriptor set limits
-    u32                                         maxBoundDescriptorSets                    /// max number of descriptors sets that can be bound to a pipeline
-    u32                                         maxPerStageDescriptorSamplers             /// max num of samplers allowed per-stage in a descriptor set
-    u32                                         maxPerStageDescriptorUniformBuffers       /// max num of uniform buffers allowed per-stage in a descriptor set
-    u32                                         maxPerStageDescriptorStorageBuffers       /// max num of storage buffers allowed per-stage in a descriptor set
-    u32                                         maxPerStageDescriptorSampledImages        /// max num of sampled images allowed per-stage in a descriptor set
-    u32                                         maxPerStageDescriptorStorageImages        /// max num of storage images allowed per-stage in a descriptor set
-    u32                                         maxPerStageDescriptorInputAttachments
-    u32                                         maxPerStageResources
-    u32                                         maxDescriptorSetSamplers                  /// max num of samplers allowed in all stages in a descriptor set
-    u32                                         maxDescriptorSetUniformBuffers            /// max num of uniform buffers allowed in all stages in a descriptor set
-    u32                                         maxDescriptorSetUniformBuffersDynamic     /// max num of dynamic uniform buffers allowed in all stages in a descriptor set
-    u32                                         maxDescriptorSetStorageBuffers            /// max num of storage buffers allowed in all stages in a descriptor set
-    u32                                         maxDescriptorSetStorageBuffersDynamic     /// max num of dynamic storage buffers allowed in all stages in a descriptor set
-    u32                                         maxDescriptorSetSampledImages             /// max num of sampled images allowed in all stages in a descriptor set
-    u32                                         maxDescriptorSetStorageImages             /// max num of storage images allowed in all stages in a descriptor set
-    u32                                         maxDescriptorSetInputAttachments
-    /// vertex stage limits
-    u32                                         maxVertexInputAttributes                  /// max num of vertex input attribute slots
-    u32                                         maxVertexInputBindings                    /// max num of vertex input binding slots
-    u32                                         maxVertexInputAttributeOffset             /// max vertex input attribute offset added to vertex buffer offset
-    u32                                         maxVertexInputBindingStride               /// max vertex input binding stride
-    u32                                         maxVertexOutputComponents                 /// max num of output components written by vertex shader
-    /// tessellation control stage limits
-    u32                                         maxTessellationGenerationLevel                  /// max level supported by tess primitive generator
-    u32                                         maxTessellationPatchSize                        /// max patch size (vertices)
-    u32                                         maxTessellationControlPerVertexInputComponents  /// max num of input components per-vertex in TCS
-    u32                                         maxTessellationControlPerVertexOutputComponents /// max num of output components per-vertex in TCS
-    u32                                         maxTessellationControlPerPatchOutputComponents  /// max num of output components per-patch in TCS
-    u32                                         maxTessellationControlTotalOutputComponents     /// max total num of per-vertex and per-patch output components in TCS
-    u32                                         maxTessellationEvaluationInputComponents        /// max num of input components per vertex in TES
-    u32                                         maxTessellationEvaluationOutputComponents       /// max num of output components per vertex in TES
-    /// geometry stage limits
-    u32                                         maxGeometryShaderInvocations              /// max invocation count supported in geometry shader
-    u32                                         maxGeometryInputComponents                /// max num of input components read in geometry stage
-    u32                                         maxGeometryOutputComponents               /// max num of output components written in geometry stage
-    u32                                         maxGeometryOutputVertices                 /// max num of vertices that can be emitted in geometry stage
-    u32                                         maxGeometryTotalOutputComponents          /// max total num of components (all vertices) written in geometry stage
-    /// fragment stage limits
-    u32                                         maxFragmentInputComponents                /// max num of input compontents read in fragment stage
-    u32                                         maxFragmentOutputAttachments              /// max num of output attachments written in fragment stage
-    u32                                         maxFragmentDualSrcAttachments             /// max num of output attachments written when using dual source blending
-    u32                                         maxFragmentCombinedOutputResources        /// max total num of storage buffers, storage images and output buffers
-    /// compute stage limits
-    u32                                         maxComputeSharedMemorySize                /// max total storage size of work group local storage (bytes)
-    u32[3]                                      maxComputeWorkGroupCount                  /// max num of compute work groups that may be dispatched by a single command (x,y,z)
-    u32                                         maxComputeWorkGroupInvocations            /// max total compute invocations in a single local work group
-    u32[3]                                      maxComputeWorkGroupSize                   /// max local size of a compute work group (x,y,z)
-
-    u32                                         subPixelPrecisionBits                     /// num bits of subpixel precision in screen x and y
-    u32                                         subTexelPrecisionBits                     /// num bits of subtexel precision
-    u32                                         mipmapPrecisionBits                       /// num bits of mipmap precision
-
-    u32                                         maxDrawIndexedIndexValue                  /// max index value for indexed draw calls (for 32-bit indices)
-    u32                                         maxDrawIndirectCount
-
-    f32                                         maxSamplerLodBias                         /// max absolute sampler level of detail bias
-    f32                                         maxSamplerAnisotropy                      /// max degree of sampler anisotropy
-
-    u32                                         maxViewports                              /// max number of active viewports
-    u32[2]                                      maxViewportDimensions                     /// max viewport dimensions (x,y)
-    f32[2]                                      viewportBoundsRange                       /// viewport bounds range (min,max)
-    u32                                         viewportSubPixelBits                      /// num bits of subpixel precision for viewport
-
-    platform.size_t                             minMemoryMapAlignment                     /// min required alignment of pointers returned by MapMemory (bytes)
-    VkDeviceSize                                minTexelBufferOffsetAlignment             /// min required alignment for texel buffer offsets (bytes)
-    VkDeviceSize                                minUniformBufferOffsetAlignment           /// min required alignment for uniform buffer sizes and offsets (bytes)
-    VkDeviceSize                                minStorageBufferOffsetAlignment           /// min required alignment for storage buffer offsets (bytes)
-
-    s32                                         minTexelOffset                            /// min texel offset for OpTextureSampleOffset
-    u32                                         maxTexelOffset                            /// max texel offset for OpTextureSampleOffset
-    s32                                         minTexelGatherOffset                      /// min texel offset for OpTextureGatherOffset
-    u32                                         maxTexelGatherOffset                      /// max texel offset for OpTextureGatherOffset
-    f32                                         minInterpolationOffset                    /// furthest negative offset for interpolateAtOffset
-    f32                                         maxInterpolationOffset                    /// furthest positive offset for interpolateAtOffset
-    u32                                         subPixelInterpolationOffsetBits           /// num of subpixel bits for interpolateAtOffset
-
-    u32                                         maxFramebufferWidth                       /// max width for a framebuffer
-    u32                                         maxFramebufferHeight                      /// max height for a framebuffer
-    u32                                         maxFramebufferLayers                      /// max layer count for a layered framebuffer
-    VkSampleCountFlags                          framebufferColorSampleCounts
-    VkSampleCountFlags                          framebufferDepthSampleCounts
-    VkSampleCountFlags                          framebufferStencilSampleCounts
-    VkSampleCountFlags                          framebufferNoAttachmentSampleCounts
-    u32                                         maxColorAttachments                       /// max num of framebuffer color attachments
-
-    VkSampleCountFlags                          sampledImageColorSampleCounts
-    VkSampleCountFlags                          sampledImageIntegerSampleCounts
-    VkSampleCountFlags                          sampledImageDepthSampleCounts
-    VkSampleCountFlags                          sampledImageStencilSampleCounts
-    VkSampleCountFlags                          storageImageSampleCounts
-    u32                                         maxSampleMaskWords                        /// max num of sample mask words
-    VkBool32                                    timestampComputeAndGraphics
-
-    f32                                         timestampPeriod
-
-    u32                                         maxClipDistances                          /// max number of clip distances
-    u32                                         maxCullDistances                          /// max number of cull distances
-    u32                                         maxCombinedClipAndCullDistances           /// max combined number of user clipping
-
-    u32                                         discreteQueuePriorities
-
-    f32[2]                                      pointSizeRange                            /// range (min,max) of supported point sizes
-    f32[2]                                      lineWidthRange                            /// range (min,max) of supported line widths
-    f32                                         pointSizeGranularity                      /// granularity of supported point sizes
-    f32                                         lineWidthGranularity                      /// granularity of supported line widths
-    VkBool32                                    strictLines
-    VkBool32                                    standardSampleLocations
-
-    VkDeviceSize                                optimalBufferCopyOffsetAlignment
-    VkDeviceSize                                optimalBufferCopyRowPitchAlignment
-    VkDeviceSize                                nonCoherentAtomSize
-}
-
-class VkPhysicalDeviceSparseProperties {
-    VkBool32                                    residencyStandard2DBlockShape             /// Sparse resources support: GPU will access all 2D (single sample) sparse resources using the standard block shapes (based on pixel format)
-    VkBool32                                    residencyStandard2DMultisampleBlockShape  /// Sparse resources support: GPU will access all 2D (multisample) sparse resources using the standard block shapes (based on pixel format)
-    VkBool32                                    residencyStandard3DBlockShape             /// Sparse resources support: GPU will access all 3D sparse resources using the standard block shapes (based on pixel format)
-    VkBool32                                    residencyAlignedMipSize                   /// Sparse resources support: Images with mip-level dimensions that are NOT a multiple of the block size will be placed in the mip tail
-    VkBool32                                    residencyNonResidentStrict                /// Sparse resources support: GPU can safely access non-resident regions of a resource, all reads return as if data is 0, writes are discarded
-}
-
-class VkSemaphoreCreateInfo {
-    VkStructureType                             sType      /// Must be VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
-    const void*                                 pNext      /// Pointer to next structure
-    VkSemaphoreCreateFlags                      flags      /// Semaphore creation flags
-}
-
-class VkQueryPoolCreateInfo {
-    VkStructureType                             sType              /// Must be VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO
-    const void*                                 pNext              /// Pointer to next structure
-    VkQueryPoolCreateFlags                      flags
-    VkQueryType                                 queryType
-    u32                                         queryCount
-    VkQueryPipelineStatisticFlags               pipelineStatistics /// Optional
-}
-
-class VkFramebufferCreateInfo {
-    VkStructureType                             sType  /// Must be VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO
-    const void*                                 pNext  /// Pointer to next structure
-    VkFramebufferCreateFlags                    flags
-    VkRenderPass                                renderPass
-    u32                                         attachmentCount
-    const VkImageView*                          pAttachments
-    u32                                         width
-    u32                                         height
-    u32                                         layers
-}
-
-class VkDrawIndirectCommand {
-    u32                                         vertexCount
-    u32                                         instanceCount
-    u32                                         firstVertex
-    u32                                         firstInstance
-}
-
-class VkDrawIndexedIndirectCommand {
-    u32                                         indexCount
-    u32                                         instanceCount
-    u32                                         firstIndex
-    s32                                         vertexOffset
-    u32                                         firstInstance
-}
-
-class VkDispatchIndirectCommand {
-    u32                                         x
-    u32                                         y
-    u32                                         z
-}
-
-class VkBaseOutStructure {
-    VkStructureType                             sType
-    void*                                       pNext
-}
-
-class VkBaseInStructure {
-    VkStructureType                             sType
-    const void*                                 pNext
-}
-
-//@vulkan1_1 structures
-
-class VkPhysicalDeviceSubgroupProperties {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         subgroupSize
-    VkShaderStageFlags                          supportedStages
-    VkSubgroupFeatureFlags                      supportedOperations
-    VkBool32                                    quadOperationsInAllStages
-}
-
-class VkBindBufferMemoryInfo {
-    VkStructureType    sType
-    const void*        pNext
-    VkBuffer           buffer
-    VkDeviceMemory     memory
-    VkDeviceSize       memoryOffset
-}
-
-class VkBindImageMemoryInfo {
-    VkStructureType    sType
-    const void*        pNext
-    VkImage            image
-    VkDeviceMemory     memory
-    VkDeviceSize       memoryOffset
-}
-
-class VkPhysicalDevice16BitStorageFeatures {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           storageBuffer16BitAccess
-    VkBool32           uniformAndStorageBuffer16BitAccess
-    VkBool32           storagePushConstant16
-    VkBool32           storageInputOutput16
-}
-
-class VkMemoryDedicatedRequirements {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           prefersDedicatedAllocation
-    VkBool32           requiresDedicatedAllocation
-}
-
-class VkMemoryDedicatedAllocateInfo {
-    VkStructureType    sType
-    const void*        pNext
-    VkImage            image
-    VkBuffer           buffer
-}
-
-class VkMemoryAllocateFlagsInfo {
-    VkStructureType          sType
-    const void*              pNext
-    VkMemoryAllocateFlags    flags
-    u32                      deviceMask
-}
-
-class VkDeviceGroupRenderPassBeginInfo {
-    VkStructureType    sType
-    const void*        pNext
-    u32                deviceMask
-    u32                deviceRenderAreaCount
-    const VkRect2D*    pDeviceRenderAreas
-}
-
-class VkDeviceGroupCommandBufferBeginInfo {
-    VkStructureType    sType
-    const void*        pNext
-    u32                deviceMask
-}
-
-class VkDeviceGroupSubmitInfo {
-    VkStructureType             sType
-    const void*                 pNext
-    u32                         waitSemaphoreCount
-    const u32*                  pWaitSemaphoreDeviceIndices
-    u32                         commandBufferCount
-    const u32*                  pCommandBufferDeviceMasks
-    u32                         signalSemaphoreCount
-    const u32*                  pSignalSemaphoreDeviceIndices
-}
-
-class VkDeviceGroupBindSparseInfo {
-    VkStructureType    sType
-    const void*        pNext
-    u32                resourceDeviceIndex
-    u32                memoryDeviceIndex
-}
-
-class VkBindBufferMemoryDeviceGroupInfo {
-    VkStructureType                     sType
-    const void*                         pNext
-    u32                                 deviceIndexCount
-    const u32*                          pDeviceIndices
-}
-
-class VkBindImageMemoryDeviceGroupInfo {
-    VkStructureType                     sType
-    const void*                         pNext
-    u32                                 deviceIndexCount
-    const u32*                          pDeviceIndices
-    u32                                 splitInstanceBindRegionCount
-    const VkRect2D*                     pSplitInstanceBindRegions
-}
-
-class VkPhysicalDeviceGroupProperties {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         physicalDeviceCount
-    VkPhysicalDevice[VK_MAX_DEVICE_GROUP_SIZE]  physicalDevices
-    VkBool32                                    subsetAllocation
-}
-
-class VkDeviceGroupDeviceCreateInfo {
-    VkStructureType            sType
-    const void*                pNext
-    u32                        physicalDeviceCount
-    const VkPhysicalDevice*    pPhysicalDevices
-}
-
-class VkBufferMemoryRequirementsInfo2 {
-    VkStructureType    sType
-    const void*        pNext
-    VkBuffer           buffer
-}
-
-class VkImageMemoryRequirementsInfo2 {
-    VkStructureType    sType
-    const void*        pNext
-    VkImage            image
-}
-
-class VkImageSparseMemoryRequirementsInfo2 {
-    VkStructureType    sType
-    const void*        pNext
-    VkImage            image
-}
-
-class VkMemoryRequirements2 {
-    VkStructureType         sType
-    void*                   pNext
-    VkMemoryRequirements    memoryRequirements
-}
-
-class VkSparseImageMemoryRequirements2 {
-    VkStructureType                    sType
-    void*                              pNext
-    VkSparseImageMemoryRequirements    memoryRequirements
-}
-
-class VkPhysicalDeviceFeatures2 {
-    VkStructureType             sType
-    void*                       pNext
-    VkPhysicalDeviceFeatures    features
-}
-
-class VkPhysicalDeviceProperties2 {
-    VkStructureType               sType
-    void*                         pNext
-    VkPhysicalDeviceProperties    properties
-}
-
-class VkFormatProperties2 {
-    VkStructureType       sType
-    void*                 pNext
-    VkFormatProperties    formatProperties
-}
-
-class VkImageFormatProperties2 {
-    VkStructureType            sType
-    void*                      pNext
-    VkImageFormatProperties    imageFormatProperties
-}
-
-class VkPhysicalDeviceImageFormatInfo2 {
-    VkStructureType       sType
-    const void*           pNext
-    VkFormat              format
-    VkImageType           type
-    VkImageTiling         tiling
-    VkImageUsageFlags     usage
-    VkImageCreateFlags    flags
-}
-
-class VkQueueFamilyProperties2 {
-    VkStructureType            sType
-    void*                      pNext
-    VkQueueFamilyProperties    queueFamilyProperties
-}
-
-class VkPhysicalDeviceMemoryProperties2 {
-    VkStructureType                     sType
-    void*                               pNext
-    VkPhysicalDeviceMemoryProperties    memoryProperties
-}
-
-class VkSparseImageFormatProperties2 {
-    VkStructureType                  sType
-    void*                            pNext
-    VkSparseImageFormatProperties    properties
-}
-
-class VkPhysicalDeviceSparseImageFormatInfo2 {
-    VkStructureType          sType
-    const void*              pNext
-    VkFormat                 format
-    VkImageType              type
-    VkSampleCountFlagBits    samples
-    VkImageUsageFlags        usage
-    VkImageTiling            tiling
-}
-
-class VkPhysicalDevicePointClippingProperties {
-    VkStructureType            sType
-    void*                      pNext
-    VkPointClippingBehavior    pointClippingBehavior
-}
-
-class VkInputAttachmentAspectReference {
-    u32                   subpass
-    u32                   inputAttachmentIndex
-    VkImageAspectFlags    aspectMask
-}
-
-class VkRenderPassInputAttachmentAspectCreateInfo {
-    VkStructureType                            sType
-    const void*                                pNext
-    u32                                        aspectReferenceCount
-    const VkInputAttachmentAspectReference*    pAspectReferences
-}
-
-class VkImageViewUsageCreateInfo {
-    VkStructureType      sType
-    const void*          pNext
-    VkImageUsageFlags    usage
-}
-
-class VkPipelineTessellationDomainOriginStateCreateInfo {
-    VkStructureType               sType
-    const void*                   pNext
-    VkTessellationDomainOrigin    domainOrigin
-}
-
-class VkRenderPassMultiviewCreateInfo {
-    VkStructureType    sType
-    const void*        pNext
-    u32                subpassCount
-    const u32*         pViewMasks
-    u32                dependencyCount
-    const s32*         pViewOffsets
-    u32                correlationMaskCount
-    const u32*         pCorrelationMasks
-}
-
-class VkPhysicalDeviceMultiviewFeatures {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           multiview
-    VkBool32           multiviewGeometryShader
-    VkBool32           multiviewTessellationShader
-}
-
-class VkPhysicalDeviceMultiviewProperties {
-    VkStructureType    sType
-    void*              pNext
-    u32                maxMultiviewViewCount
-    u32                maxMultiviewInstanceIndex
-}
-
-class VkPhysicalDeviceVariablePointerFeatures {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           variablePointersStorageBuffer
-    VkBool32           variablePointers
-}
-
-class VkPhysicalDeviceProtectedMemoryFeatures {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           protectedMemory
-}
-
-class VkPhysicalDeviceProtectedMemoryProperties {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           protectedNoFault
-}
-
-class VkDeviceQueueInfo2 {
-    VkStructureType             sType
-    const void*                 pNext
-    VkDeviceQueueCreateFlags    flags
-    u32                         queueFamilyIndex
-    u32                         queueIndex
-}
-
-class VkProtectedSubmitInfo {
-    VkStructureType    sType
-    const void*        pNext
-    VkBool32           protectedSubmit
-}
-
-class VkSamplerYcbcrConversionCreateInfo {
-    VkStructureType                  sType
-    const void*                      pNext
-    VkFormat                         format
-    VkSamplerYcbcrModelConversion    ycbcrModel
-    VkSamplerYcbcrRange              ycbcrRange
-    VkComponentMapping               components
-    VkChromaLocation                 xChromaOffset
-    VkChromaLocation                 yChromaOffset
-    VkFilter                         chromaFilter
-    VkBool32                         forceExplicitReconstruction
-}
-
-class VkSamplerYcbcrConversionInfo {
-    VkStructureType             sType
-    const void*                 pNext
-    VkSamplerYcbcrConversion    conversion
-}
-
-class VkBindImagePlaneMemoryInfo {
-    VkStructureType          sType
-    const void*              pNext
-    VkImageAspectFlagBits    planeAspect
-}
-
-class VkImagePlaneMemoryRequirementsInfo {
-    VkStructureType          sType
-    const void*              pNext
-    VkImageAspectFlagBits    planeAspect
-}
-
-class VkPhysicalDeviceSamplerYcbcrConversionFeatures {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           samplerYcbcrConversion
-}
-
-class VkSamplerYcbcrConversionImageFormatProperties {
-    VkStructureType    sType
-    void*              pNext
-    u32                combinedImageSamplerDescriptorCount
-}
-
-class VkDescriptorUpdateTemplateEntry {
-    u32                                 dstBinding
-    u32                                 dstArrayElement
-    u32                                 descriptorCount
-    VkDescriptorType                    descriptorType
-    platform.size_t                     offset
-    platform.size_t                     stride
-}
-
-class VkDescriptorUpdateTemplateCreateInfo {
-    VkStructureType                           sType
-    const void*                               pNext
-    VkDescriptorUpdateTemplateCreateFlags     flags
-    u32                                       descriptorUpdateEntryCount
-    const VkDescriptorUpdateTemplateEntry*    pDescriptorUpdateEntries
-    VkDescriptorUpdateTemplateType            templateType
-    VkDescriptorSetLayout                     descriptorSetLayout
-    VkPipelineBindPoint                       pipelineBindPoint
-    VkPipelineLayout                          pipelineLayout
-    u32                                       set
-}
-
-class VkExternalMemoryProperties {
-    VkExternalMemoryFeatureFlags       externalMemoryFeatures
-    VkExternalMemoryHandleTypeFlags    exportFromImportedHandleTypes
-    VkExternalMemoryHandleTypeFlags    compatibleHandleTypes
-}
-
-class VkPhysicalDeviceExternalImageFormatInfo {
-    VkStructureType                       sType
-    const void*                           pNext
-    VkExternalMemoryHandleTypeFlagBits    handleType
-}
-
-class VkExternalImageFormatProperties {
-    VkStructureType               sType
-    void*                         pNext
-    VkExternalMemoryProperties    externalMemoryProperties
-}
-
-class VkPhysicalDeviceExternalBufferInfo {
-    VkStructureType                       sType
-    const void*                           pNext
-    VkBufferCreateFlags                   flags
-    VkBufferUsageFlags                    usage
-    VkExternalMemoryHandleTypeFlagBits    handleType
-}
-
-class VkExternalBufferProperties {
-    VkStructureType               sType
-    void*                         pNext
-    VkExternalMemoryProperties    externalMemoryProperties
-}
-
-class VkPhysicalDeviceIDProperties {
-    VkStructureType    sType
-    void*              pNext
-    u8[VK_UUID_SIZE]   deviceUUID
-    u8[VK_UUID_SIZE]   driverUUID
-    u8[VK_LUID_SIZE]   deviceLUID
-    u32                deviceNodeMask
-    VkBool32           deviceLUIDValid
-}
-
-class VkExternalMemoryImageCreateInfo {
-    VkStructureType                    sType
-    const void*                        pNext
-    VkExternalMemoryHandleTypeFlags    handleTypes
-}
-
-class VkExternalMemoryBufferCreateInfo {
-    VkStructureType                    sType
-    const void*                        pNext
-    VkExternalMemoryHandleTypeFlags    handleTypes
-}
-
-class VkExportMemoryAllocateInfo {
-    VkStructureType                    sType
-    const void*                        pNext
-    VkExternalMemoryHandleTypeFlags    handleTypes
-}
-
-class VkPhysicalDeviceExternalFenceInfo {
-    VkStructureType                      sType
-    const void*                          pNext
-    VkExternalFenceHandleTypeFlagBits    handleType
-}
-
-class VkExternalFenceProperties {
-    VkStructureType                   sType
-    void*                             pNext
-    VkExternalFenceHandleTypeFlags    exportFromImportedHandleTypes
-    VkExternalFenceHandleTypeFlags    compatibleHandleTypes
-    VkExternalFenceFeatureFlags       externalFenceFeatures
-}
-
-class VkExportFenceCreateInfo {
-    VkStructureType                   sType
-    const void*                       pNext
-    VkExternalFenceHandleTypeFlags    handleTypes
-}
-
-class VkExportSemaphoreCreateInfo {
-    VkStructureType                       sType
-    const void*                           pNext
-    VkExternalSemaphoreHandleTypeFlags    handleTypes
-}
-
-class VkPhysicalDeviceExternalSemaphoreInfo {
-    VkStructureType                          sType
-    const void*                              pNext
-    VkExternalSemaphoreHandleTypeFlagBits    handleType
-}
-
-class VkExternalSemaphoreProperties {
-    VkStructureType                       sType
-    void*                                 pNext
-    VkExternalSemaphoreHandleTypeFlags    exportFromImportedHandleTypes
-    VkExternalSemaphoreHandleTypeFlags    compatibleHandleTypes
-    VkExternalSemaphoreFeatureFlags       externalSemaphoreFeatures
-}
-
-class VkPhysicalDeviceMaintenance3Properties {
-    VkStructureType    sType
-    void*              pNext
-    u32                maxPerSetDescriptors
-    VkDeviceSize       maxMemoryAllocationSize
-}
-
-class VkDescriptorSetLayoutSupport {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           supported
-}
-
-class VkPhysicalDeviceShaderDrawParameterFeatures {
-    VkStructureType    sType
-    void*              pNext
-    VkBool32           shaderDrawParameters
-}
-
-
-@extension("VK_KHR_surface") // 1
-class VkSurfaceCapabilitiesKHR {
-    u32                                         minImageCount
-    u32                                         maxImageCount
-    VkExtent2D                                  currentExtent
-    VkExtent2D                                  minImageExtent
-    VkExtent2D                                  maxImageExtent
-    u32                                         maxImageArrayLayers
-    VkSurfaceTransformFlagsKHR                  supportedTransforms
-    VkSurfaceTransformFlagBitsKHR               currentTransform
-    VkCompositeAlphaFlagsKHR                    supportedCompositeAlpha
-    VkImageUsageFlags                           supportedUsageFlags
-}
-
-@extension("VK_KHR_surface") // 1
-class VkSurfaceFormatKHR {
-    VkFormat                                    format
-    VkColorSpaceKHR                             colorSpace
-}
-
-@extension("VK_KHR_swapchain") // 2
-class VkSwapchainCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSwapchainCreateFlagsKHR                   flags
-    VkSurfaceKHR                                surface
-    u32                                         minImageCount
-    VkFormat                                    imageFormat
-    VkColorSpaceKHR                             imageColorSpace
-    VkExtent2D                                  imageExtent
-    u32                                         imageArrayLayers
-    VkImageUsageFlags                           imageUsage
-    VkSharingMode                               sharingMode
-    u32                                         queueFamilyIndexCount
-    const u32*                                  pQueueFamilyIndices
-    VkSurfaceTransformFlagBitsKHR               preTransform
-    VkCompositeAlphaFlagBitsKHR                 compositeAlpha
-    VkPresentModeKHR                            presentMode
-    VkBool32                                    clipped
-    VkSwapchainKHR                              oldSwapchain
-}
-
-@extension("VK_KHR_swapchain") // 2
-class VkPresentInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         waitSemaphoreCount
-    const VkSemaphore*                          pWaitSemaphores
-    u32                                         swapchainCount
-    const VkSwapchainKHR*                       pSwapchains
-    const u32*                                  pImageIndices
-    VkResult*                                   pResults
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-class VkImageSwapchainCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSwapchainKHR                              swapchain
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-class VkBindImageMemorySwapchainInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSwapchainKHR                              swapchain
-    u32                                         imageIndex
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-class VkAcquireNextImageInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSwapchainKHR                              swapchain
-    u64                                         timeout
-    VkSemaphore                                 semaphore
-    VkFence                                     fence
-    u32                                         deviceMask
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-class VkDeviceGroupPresentCapabilitiesKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32[VK_MAX_DEVICE_GROUP_SIZE]               presentMask
-    VkDeviceGroupPresentModeFlagsKHR            modes
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-class VkDeviceGroupPresentInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         swapchainCount
-    const u32*                                  pDeviceMasks
-    VkDeviceGroupPresentModeFlagBitsKHR         mode
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-class VkDeviceGroupSwapchainCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDeviceGroupPresentModeFlagsKHR            modes
-}
-
-@extension("VK_KHR_display") // 3
-class VkDisplayPropertiesKHR {
-    VkDisplayKHR                                display
-    const char*                                 displayName
-    VkExtent2D                                  physicalDimensions
-    VkExtent2D                                  physicalResolution
-    VkSurfaceTransformFlagsKHR                  supportedTransforms
-    VkBool32                                    planeReorderPossible
-    VkBool32                                    persistentContent
-}
-
-@extension("VK_KHR_display") // 3
-class VkDisplayModeParametersKHR {
-    VkExtent2D                                  visibleRegion
-    u32                                         refreshRate
-}
-
-@extension("VK_KHR_display") // 3
-class VkDisplayModePropertiesKHR {
-    VkDisplayModeKHR                            displayMode
-    VkDisplayModeParametersKHR                  parameters
-}
-
-@extension("VK_KHR_display") // 3
-class VkDisplayModeCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDisplayModeCreateFlagsKHR                 flags
-    VkDisplayModeParametersKHR                  parameters
-}
-
-@extension("VK_KHR_display") // 3
-class VkDisplayPlanePropertiesKHR {
-    VkDisplayKHR                                currentDisplay
-    u32                                         currentStackIndex
-}
-
-@extension("VK_KHR_display") // 3
-class VkDisplayPlaneCapabilitiesKHR {
-    VkDisplayPlaneAlphaFlagsKHR                 supportedAlpha
-    VkOffset2D                                  minSrcPosition
-    VkOffset2D                                  maxSrcPosition
-    VkExtent2D                                  minSrcExtent
-    VkExtent2D                                  maxSrcExtent
-    VkOffset2D                                  minDstPosition
-    VkOffset2D                                  maxDstPosition
-    VkExtent2D                                  minDstExtent
-    VkExtent2D                                  maxDstExtent
-}
-
-@extension("VK_KHR_display") // 3
-class VkDisplaySurfaceCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDisplaySurfaceCreateFlagsKHR              flags
-    VkDisplayModeKHR                            displayMode
-    u32                                         planeIndex
-    u32                                         planeStackIndex
-    VkSurfaceTransformFlagBitsKHR               transform
-    f32                                         globalAlpha
-    VkDisplayPlaneAlphaFlagBitsKHR              alphaMode
-    VkExtent2D                                  imageExtent
-}
-
-@extension("VK_KHR_display_swapchain") // 4
-class VkDisplayPresentInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkRect2D                                    srcRect
-    VkRect2D                                    dstRect
-    VkBool32                                    persistent
-}
-
-@extension("VK_KHR_xlib_surface") // 5
-class VkXlibSurfaceCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkXlibSurfaceCreateFlagsKHR                 flags
-    platform.Display*                           dpy
-    platform.Window                             window
-}
-
-@extension("VK_KHR_xcb_surface") // 6
-class VkXcbSurfaceCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkXcbSurfaceCreateFlagsKHR                  flags
-    platform.xcb_connection_t*                  connection
-    platform.xcb_window_t                       window
-}
-
-@extension("VK_KHR_wayland_surface") // 7
-class VkWaylandSurfaceCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkWaylandSurfaceCreateFlagsKHR              flags
-    platform.wl_display*                        display
-    platform.wl_surface*                        surface
-}
-
-@extension("VK_KHR_android_surface") // 9
-class VkAndroidSurfaceCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkAndroidSurfaceCreateFlagsKHR              flags
-    platform.ANativeWindow*                     window
-}
-
-@extension("VK_KHR_win32_surface") // 10
-class VkWin32SurfaceCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkWin32SurfaceCreateFlagsKHR                flags
-    platform.HINSTANCE                          hinstance
-    platform.HWND                               hwnd
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-@internal class Gralloc1Usage {
-    u64                                         consumer
-    u64                                         producer
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-class VkNativeBufferANDROID {
-    VkStructureType                             sType
-    const void*                                 pNext
-    platform.buffer_handle_t                    handle
-    s32                                         stride
-    s32                                         format
-    s32                                         usage
-    Gralloc1Usage                               usage2
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-class VkSwapchainImageCreateInfoANDROID {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSwapchainImageUsageFlagsANDROID           flags
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-class VkPhysicalDevicePresentationPropertiesANDROID {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    sharedImage
-}
-
-@extension("VK_EXT_debug_report") // 12
-class VkDebugReportCallbackCreateInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDebugReportFlagsEXT                       flags
-    PFN_vkDebugReportCallbackEXT                pfnCallback
-    void*                                       pUserData
-}
-
-@extension("VK_AMD_rasterization_order") // 19
-class VkPipelineRasterizationStateRasterizationOrderAMD {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkRasterizationOrderAMD                     rasterizationOrder
-}
-
-@extension("VK_EXT_debug_marker") // 23
-class VkDebugMarkerObjectNameInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDebugReportObjectTypeEXT                  objectType
-    u64                                         object
-    const char*                                 pObjectName
-}
-
-@extension("VK_EXT_debug_marker") // 23
-class VkDebugMarkerObjectTagInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDebugReportObjectTypeEXT                  objectType
-    u64                                         object
-    u64                                         tagName
-    platform.size_t                             tagSize
-    const void*                                 pTag
-}
-
-@extension("VK_EXT_debug_marker") // 23
-class VkDebugMarkerMarkerInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    const char*                                 pMarkerName
-    f32[4]                                      color
-}
-
-@extension("VK_NV_dedicated_allocation") // 27
-class VkDedicatedAllocationImageCreateInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkBool32                                    dedicatedAllocation
-}
-
-@extension("VK_NV_dedicated_allocation") // 27
-class VkDedicatedAllocationBufferCreateInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkBool32                                    dedicatedAllocation
-}
-
-@extension("VK_NV_dedicated_allocation") // 27
-class VkDedicatedAllocationMemoryAllocateInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkImage                                     image
-    VkBuffer                                    buffer
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-class VkPhysicalDeviceTransformFeedbackFeaturesEXT {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    transformFeedback
-    VkBool32                                    geometryStreams
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-class VkPhysicalDeviceTransformFeedbackPropertiesEXT {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         maxTransformFeedbackStreams
-    u32                                         maxTransformFeedbackBuffers
-    VkDeviceSize                                maxTransformFeedbackBufferSize
-    u32                                         maxTransformFeedbackStreamDataSize
-    u32                                         maxTransformFeedbackBufferDataSize
-    u32                                         maxTransformFeedbackBufferDataStride
-    VkBool32                                    transformFeedbackQueries
-    VkBool32                                    transformFeedbackStreamsLinesTriangles
-    VkBool32                                    transformFeedbackRasterizationStreamSelect
-    VkBool32                                    transformFeedbackDraw
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-class VkPipelineRasterizationStateStreamCreateInfoEXT {
-    VkStructureType                                     sType
-    const void*                                         pNext
-    VkPipelineRasterizationStateStreamCreateFlagsEXT    flags
-    u32                                                 rasterizationStream
-}
-
-@extension("VK_AMD_texture_gather_bias_lod") // 42
-class VkTextureLODGatherFormatPropertiesAMD {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    supportsTextureGatherLODBiasAMD
-}
-
-@extension("VK_AMD_shader_info") // 43
-class VkShaderResourceUsageAMD {
-    u32                                         numUsedVgprs
-    u32                                         numUsedSgprs
-    u32                                         ldsSizePerLocalWorkGroup
-    platform.size_t                             ldsUsageSizeInBytes
-    platform.size_t                             scratchMemUsageInBytes
-}
-
-@extension("VK_AMD_shader_info") // 43
-class VkShaderStatisticsInfoAMD {
-    VkShaderStageFlags                          shaderStageMask
-    VkShaderResourceUsageAMD                    resourceUsage
-    u32                                         numPhysicalVgprs
-    u32                                         numPhysicalSgprs
-    u32                                         numAvailableVgprs
-    u32                                         numAvailableSgprs
-    u32[3]                                      computeWorkGroupSize
-}
-
-@extension("VK_NV_corner_sampled_image") // 51
-class VkPhysicalDeviceCornerSampledImageFeaturesNV {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    cornerSampledImage
-}
-
-@extension("VK_KHR_multiview") // 54
-class VkRenderPassMultiviewCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         subpassCount
-    const u32*                                  pViewMasks
-    u32                                         dependencyCount
-    const s32*                                  pViewOffsets
-    u32                                         correlationMaskCount
-    const u32*                                  pCorrelationMasks
-}
-
-@extension("VK_KHR_multiview") // 54
-class VkPhysicalDeviceMultiviewFeaturesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    multiview
-    VkBool32                                    multiviewGeometryShader
-    VkBool32                                    multiviewTessellationShader
-}
-
-@extension("VK_KHR_multiview") // 54
-class VkPhysicalDeviceMultiviewPropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         maxMultiviewViewCount
-    u32                                         maxMultiviewInstanceIndex
-}
-
-@extension("VK_NV_external_memory_capabilities") // 56
-class VkExternalImageFormatPropertiesNV {
-    VkImageFormatProperties                     imageFormatProperties
-    VkExternalMemoryFeatureFlagsNV              externalMemoryFeatures
-    VkExternalMemoryHandleTypeFlagsNV           exportFromImportedHandleTypes
-    VkExternalMemoryHandleTypeFlagsNV           compatibleHandleTypes
-}
-
-@extension("VK_NV_external_memory") // 57
-class VkExternalMemoryImageCreateInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagsNV           handleTypes
-}
-
-@extension("VK_NV_external_memory") // 57
-class VkExportMemoryAllocateInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagsNV           handleTypes
-}
-
-@extension("VK_NV_external_memory_win32") // 58
-class VkImportMemoryWin32HandleInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagsNV           handleType
-    platform.HANDLE                             handle
-}
-
-@extension("VK_NV_external_memory_win32") // 58
-class VkExportMemoryWin32HandleInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    const platform.SECURITY_ATTRIBUTES*         pAttributes
-    platform.DWORD                              dwAccess
-}
-
-@extension("VK_NV_win32_keyed_mutex") // 59
-class VkWin32KeyedMutexAcquireReleaseInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         acquireCount
-    const VkDeviceMemory*                       pAcquireSyncs
-    const u64*                                  pAcquireKeys
-    const u32*                                  pAcquireTimeoutMilliseconds
-    u32                                         releaseCount
-    const VkDeviceMemory*                       pReleaseSyncs
-    const u64*                                  pReleaseKeys
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkPhysicalDeviceFeatures2KHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkPhysicalDeviceFeatures                    features
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkPhysicalDeviceProperties2KHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkPhysicalDeviceProperties                  properties
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkFormatProperties2KHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkFormatProperties                          formatProperties
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkImageFormatProperties2KHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkImageFormatProperties                     imageFormatProperties
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkPhysicalDeviceImageFormatInfo2KHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkFormat                                    format
-    VkImageType                                 type
-    VkImageTiling                               tiling
-    VkImageUsageFlags                           usage
-    VkImageCreateFlags                          flags
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkQueueFamilyProperties2KHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkQueueFamilyProperties                     queueFamilyProperties
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkPhysicalDeviceMemoryProperties2KHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkPhysicalDeviceMemoryProperties            memoryProperties
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkSparseImageFormatProperties2KHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkSparseImageFormatProperties               properties
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-class VkPhysicalDeviceSparseImageFormatInfo2KHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkFormat                                    format
-    VkImageType                                 type
-    VkSampleCountFlagBits                       samples
-    VkImageUsageFlags                           usage
-    VkImageTiling                               tiling
-}
-
-@extension("VK_KHR_device_group") // 61
-class VkMemoryAllocateFlagsInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkMemoryAllocateFlagsKHR                    flags
-    u32                                         deviceMask
-}
-
-@extension("VK_KHR_device_group") // 61
-class VkBindBufferMemoryDeviceGroupInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         deviceIndexCount
-    const u32*                                  pDeviceIndices
-}
-
-@extension("VK_KHR_device_group") // 61
-class VkBindImageMemoryDeviceGroupInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         deviceIndexCount
-    const u32*                                  pDeviceIndices
-    u32                                         SFRRectCount
-    const VkRect2D*                             pSFRRects
-}
-
-@extension("VK_KHR_device_group") // 61
-class VkDeviceGroupRenderPassBeginInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         deviceMask
-    u32                                         deviceRenderAreaCount
-    const VkRect2D*                             pDeviceRenderAreas
-}
-
-@extension("VK_KHR_device_group") // 61
-class VkDeviceGroupCommandBufferBeginInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         deviceMask
-}
-
-@extension("VK_KHR_device_group") // 61
-class VkDeviceGroupSubmitInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         waitSemaphoreCount
-    const u32*                                  pWaitSemaphoreDeviceIndices
-    u32                                         commandBufferCount
-    const u32*                                  pCommandBufferDeviceMasks
-    u32                                         signalSemaphoreCount
-    const u32*                                  pSignalSemaphoreDeviceIndices
-}
-
-@extension("VK_KHR_device_group") // 61
-class VkDeviceGroupBindSparseInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         resourceDeviceIndex
-    u32                                         memoryDeviceIndex
-}
-
-@extension("VK_EXT_validation_flags") // 62
-class VkValidationFlagsEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         disabledValidationCheckCount
-    const VkValidationCheckEXT*                 pDisabledValidationChecks
-}
-
-@extension("VK_NN_vi_surface") // 63
-class VkViSurfaceCreateInfoNN {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkViSurfaceCreateFlagsNN                    flags
-    void*                                       window
-}
-
-@extension("VK_EXT_astc_decode_mode") // 68
-class VkImageViewASTCDecodeModeEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkFormat                                    decodeMode
-}
-
-@extension("VK_EXT_astc_decode_mode") // 68
-class VkPhysicalDeviceASTCDecodeFeaturesEXT {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    decodeModeSharedExponent
-}
-
-@extension("VK_KHR_device_group_creation") // 71
-class VkPhysicalDeviceGroupPropertiesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             physicalDeviceCount
-    VkPhysicalDevice[VK_MAX_DEVICE_GROUP_SIZE]      physicalDevices
-    VkBool32                                        subsetAllocation
-}
-
-@extension("VK_KHR_device_group_creation") // 71
-class VkDeviceGroupDeviceCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         physicalDeviceCount
-    const VkPhysicalDevice*                     pPhysicalDevices
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-class VkExternalMemoryPropertiesKHR {
-    VkExternalMemoryFeatureFlagsKHR             externalMemoryFeatures
-    VkExternalMemoryHandleTypeFlagsKHR          exportFromImportedHandleTypes
-    VkExternalMemoryHandleTypeFlagsKHR          compatibleHandleTypes
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-class VkPhysicalDeviceExternalImageFormatInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagBitsKHR       handleType
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-class VkExternalImageFormatPropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkExternalMemoryPropertiesKHR               externalMemoryProperties
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-class VkPhysicalDeviceExternalBufferInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkBufferCreateFlags                         flags
-    VkBufferUsageFlags                          usage
-    VkExternalMemoryHandleTypeFlagBitsKHR       handleType
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-class VkExternalBufferPropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkExternalMemoryPropertiesKHR               externalMemoryProperties
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-class VkPhysicalDeviceIDPropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    u8[VK_UUID_SIZE]                            deviceUUID
-    u8[VK_UUID_SIZE]                            driverUUID
-    u8[VK_LUID_SIZE]                            deviceLUID
-    u32                                         deviceNodeMask
-    VkBool32                                    deviceLUIDValid
-}
-
-@extension("VK_KHR_external_memory") // 73
-class VkExternalMemoryImageCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagsKHR          handleTypes
-}
-
-@extension("VK_KHR_external_memory") // 73
-class VkExternalMemoryBufferCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagsKHR          handleTypes
-}
-
-@extension("VK_KHR_external_memory") // 73
-class VkExportMemoryAllocateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagsKHR          handleTypes
-}
-
-@extension("VK_KHR_external_memory_win32") // 74
-class VkImportMemoryWin32HandleInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagBitsKHR       handleType
-    platform.HANDLE                             handle
-    platform.LPCWSTR                            name
-}
-
-@extension("VK_KHR_external_memory_win32") // 74
-class VkExportMemoryWin32HandleInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    const platform.SECURITY_ATTRIBUTES*         pAttributes
-    platform.DWORD                              dwAccess
-    platform.LPCWSTR                            name
-}
-
-@extension("VK_KHR_external_memory_win32") // 74
-class VkMemoryWin32HandlePropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         memoryTypeBits
-}
-
-@extension("VK_KHR_external_memory_win32") // 74
-class VkMemoryGetWin32HandleInfoKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkDeviceMemory                              memory
-    VkExternalMemoryHandleTypeFlagBitsKHR       handleType
-}
-
-@extension("VK_KHR_external_memory_fd") // 75
-class VkImportMemoryFdInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalMemoryHandleTypeFlagBitsKHR       handleType
-    int                                         fd
-}
-
-@extension("VK_KHR_external_memory_fd") // 75
-class VkMemoryFdPropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         memoryTypeBits
-}
-
-@extension("VK_KHR_external_memory_fd") // 75
-class VkMemoryGetFdInfoKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkDeviceMemory                              memory
-    VkExternalMemoryHandleTypeFlagBitsKHR       handleType
-}
-
-@extension("VK_KHR_win32_keyed_mutex") // 76
-class VkWin32KeyedMutexAcquireReleaseInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         acquireCount
-    const VkDeviceMemory*                       pAcquireSyncs
-    const u64*                                  pAcquireKeys
-    const u32*                                  pAcquireTimeouts
-    u32                                         releaseCount
-    const VkDeviceMemory*                       pReleaseSyncs
-    const u64*                                  pReleaseKeys
-}
-
-@extension("VK_KHR_external_semaphore_capabilities") // 77
-class VkPhysicalDeviceExternalSemaphoreInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalSemaphoreHandleTypeFlagBitsKHR    handleType
-}
-
-@extension("VK_KHR_external_semaphore_capabilities") // 77
-class VkExternalSemaphorePropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkExternalSemaphoreHandleTypeFlagsKHR       exportFromImportedHandleTypes
-    VkExternalSemaphoreHandleTypeFlagsKHR       compatibleHandleTypes
-    VkExternalSemaphoreFeatureFlagsKHR          externalSemaphoreFeatures
-}
-
-@extension("VK_KHR_external_semaphore") // 78
-class VkExportSemaphoreCreateInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkExternalSemaphoreHandleTypeFlagsKHR       handleTypes
-}
-
-@extension("VK_KHR_external_semaphore_win32") // 79
-class VkImportSemaphoreWin32HandleInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSemaphore                                 semaphore
-    VkSemaphoreImportFlagsKHR                   flags
-    VkExternalSemaphoreHandleTypeFlagsKHR       handleType
-    platform.HANDLE                             handle
-    platform.LPCWSTR                            name
-}
-
-@extension("VK_KHR_external_semaphore_win32") // 79
-class VkExportSemaphoreWin32HandleInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    const platform.SECURITY_ATTRIBUTES*         pAttributes
-    platform.DWORD                              dwAccess
-    platform.LPCWSTR                            name
-}
-
-@extension("VK_KHR_external_semaphore_win32") // 79
-class VkD3D12FenceSubmitInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         waitSemaphoreValuesCount
-    const u64*                                  pWaitSemaphoreValues
-    u32                                         signalSemaphoreValuesCount
-    const u64*                                  pSignalSemaphoreValues
-}
-
-@extension("VK_KHR_external_semaphore_win32") // 79
-class VkSemaphoreGetWin32HandleInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSemaphore                                 semaphore
-    VkExternalSemaphoreHandleTypeFlagBitsKHR    handleType
-}
-
-@extension("VK_KHR_external_semaphore_fd") // 80
-class VkImportSemaphoreFdInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSemaphore                                 semaphore
-    VkSemaphoreImportFlagsKHR                   flags
-    VkExternalSemaphoreHandleTypeFlagBitsKHR    handleType
-    s32                                         fd
-}
-
-@extension("VK_KHR_external_semaphore_fd") // 80
-class VkSemaphoreGetFdInfoKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSemaphore                                 semaphore
-    VkExternalSemaphoreHandleTypeFlagBitsKHR    handleType
-}
-
-@extension("VK_KHR_push_descriptor") // 81
-class VkPhysicalDevicePushDescriptorPropertiesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         maxPushDescriptors
-}
-
-@extension("VK_EXT_conditional_rendering") // 82
-class VkConditionalRenderingBeginInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkBuffer                                    buffer
-    VkDeviceSize                                offset
-    VkConditionalRenderingFlagsEXT              flags
-}
-
-@extension("VK_EXT_conditional_rendering") // 82
-class VkPhysicalDeviceConditionalRenderingFeaturesEXT {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    conditionalRendering
-    VkBool32                                    inheritedConditionalRendering
-}
-
-@extension("VK_EXT_conditional_rendering") // 82
-class VkCommandBufferInheritanceConditionalRenderingInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkBool32                                    conditionalRenderingEnable
-}
-
-@extension("VK_KHR_shader_float16_int8") // 83
-class VkPhysicalDeviceFloat16Int8FeaturesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    shaderFloat16
-    VkBool32                                    shaderInt8
-}
-
-@extension("VK_KHR_16bit_storage") // 84
-class VkPhysicalDevice16BitStorageFeaturesKHR {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    storageBuffer16BitAccess
-    VkBool32                                    uniformAndStorageBuffer16BitAccess
-    VkBool32                                    storagePushConstant16
-    VkBool32                                    storageInputOutput16
-}
-
-@extension("VK_KHR_incremental_present") // 85
-class VkRectLayerKHR {
-    VkOffset2D                                  offset
-    VkExtent2D                                  extent
-    u32                                         layer
-}
-
-@extension("VK_KHR_incremental_present") // 85
-class VkPresentRegionKHR {
-    u32                                         rectangleCount
-    const VkRectLayerKHR*                       pRectangles
-}
-
-@extension("VK_KHR_incremental_present") // 85
-class VkPresentRegionsKHR {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         swapchainCount
-    const VkPresentRegionKHR*                   pRegions
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-class VkDescriptorUpdateTemplateEntryKHR {
-    u32                                         dstBinding
-    u32                                         dstArrayElement
-    u32                                         descriptorCount
-    VkDescriptorType                            descriptorType
-    platform.size_t                             offset
-    platform.size_t                             stride
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-class VkDescriptorUpdateTemplateCreateInfoKHR {
-    VkStructureType                              sType
-    void*                                        pNext
-    VkDescriptorUpdateTemplateCreateFlagsKHR     flags
-    u32                                          descriptorUpdateEntryCount
-    const VkDescriptorUpdateTemplateEntryKHR*    pDescriptorUpdateEntries
-    VkDescriptorUpdateTemplateTypeKHR            templateType
-    VkDescriptorSetLayout                        descriptorSetLayout
-    VkPipelineBindPoint                          pipelineBindPoint
-    VkPipelineLayout                             pipelineLayout
-    u32                                          set
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkDeviceGeneratedCommandsFeaturesNVX {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkBool32                                    computeBindingPointSupport
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkDeviceGeneratedCommandsLimitsNVX {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         maxIndirectCommandsLayoutTokenCount
-    u32                                         maxObjectEntryCounts
-    u32                                         minSequenceCountBufferOffsetAlignment
-    u32                                         minSequenceIndexBufferOffsetAlignment
-    u32                                         minCommandsTokenBufferOffsetAlignment
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkIndirectCommandsTokenNVX {
-    VkIndirectCommandsTokenTypeNVX              tokenType
-    VkBuffer                                    buffer
-    VkDeviceSize                                offset
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkIndirectCommandsLayoutTokenNVX {
-    VkIndirectCommandsTokenTypeNVX              tokenType
-    u32                                         bindingUnit
-    u32                                         dynamicCount
-    u32                                         divisor
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkIndirectCommandsLayoutCreateInfoNVX {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkPipelineBindPoint                         pipelineBindPoint
-    VkIndirectCommandsLayoutUsageFlagsNVX       flags
-    u32                                         tokenCount
-    const VkIndirectCommandsLayoutTokenNVX*     pTokens
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkCmdProcessCommandsInfoNVX {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkObjectTableNVX                            objectTable
-    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout
-    u32                                         indirectCommandsTokenCount
-    const VkIndirectCommandsTokenNVX*           pIndirectCommandsTokens
-    u32                                         maxSequencesCount
-    VkCommandBuffer                             targetCommandBuffer
-    VkBuffer                                    sequencesCountBuffer
-    VkDeviceSize                                sequencesCountOffset
-    VkBuffer                                    sequencesIndexBuffer
-    VkDeviceSize                                sequencesIndexOffset
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkCmdReserveSpaceForCommandsInfoNVX {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkObjectTableNVX                            objectTable
-    VkIndirectCommandsLayoutNVX                 indirectCommandsLayout
-    u32                                         maxSequencesCount
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkObjectTableCreateInfoNVX {
-    VkStructureType                             sType
-    const void*                                 pNext
-    u32                                         objectCount
-    const VkObjectEntryTypeNVX*                 pObjectEntryTypes
-    const u32*                                  pObjectEntryCounts
-    const VkObjectEntryUsageFlagsNVX*           pObjectEntryUsageFlags
-    u32                                         maxUniformBuffersPerDescriptor
-    u32                                         maxStorageBuffersPerDescriptor
-    u32                                         maxStorageImagesPerDescriptor
-    u32                                         maxSampledImagesPerDescriptor
-    u32                                         maxPipelineLayouts
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkObjectTableEntryNVX {
-    VkObjectEntryTypeNVX                        type
-    VkObjectEntryUsageFlagsNVX                  flags
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkObjectTablePipelineEntryNVX {
-    VkObjectEntryTypeNVX                        type
-    VkObjectEntryUsageFlagsNVX                  flags
-    VkPipeline                                  pipeline
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkObjectTableDescriptorSetEntryNVX {
-    VkObjectEntryTypeNVX                        type
-    VkObjectEntryUsageFlagsNVX                  flags
-    VkPipelineLayout                            pipelineLayout
-    VkDescriptorSet                             descriptorSet
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkObjectTableVertexBufferEntryNVX {
-    VkObjectEntryTypeNVX                        type
-    VkObjectEntryUsageFlagsNVX                  flags
-    VkBuffer                                    buffer
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkObjectTableIndexBufferEntryNVX {
-    VkObjectEntryTypeNVX                        type
-    VkObjectEntryUsageFlagsNVX                  flags
-    VkBuffer                                    buffer
-    VkIndexType                                 indexType
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-class VkObjectTablePushConstantEntryNVX {
-    VkObjectEntryTypeNVX                        type
-    VkObjectEntryUsageFlagsNVX                  flags
-    VkPipelineLayout                            pipelineLayout
-    VkShaderStageFlags                          stageFlags
-}
-
-@extension("VK_NV_clip_space_w_scaling") // 88
-class VkViewportWScalingNV {
-    f32                                         xcoeff
-    f32                                         ycoeff
-}
-
-@extension("VK_NV_clip_space_w_scaling") // 88
-class VkPipelineViewportWScalingStateCreateInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkBool32                                    viewportWScalingEnable
-    u32                                         viewportCount
-    const VkViewportWScalingNV*                 pViewportWScalings
-}
-
-@extension("VK_EXT_display_surface_counter") // 91
-class VkSurfaceCapabilities2EXT {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         minImageCount
-    u32                                         maxImageCount
-    VkExtent2D                                  currentExtent
-    VkExtent2D                                  minImageExtent
-    VkExtent2D                                  maxImageExtent
-    u32                                         maxImageArrayLayers
-    VkSurfaceTransformFlagsKHR                  supportedTransforms
-    VkSurfaceTransformFlagBitsKHR               currentTransform
-    VkCompositeAlphaFlagsKHR                    supportedCompositeAlpha
-    VkImageUsageFlags                           supportedUsageFlags
-    VkSurfaceCounterFlagsEXT                    supportedSurfaceCounters
-}
-
-@extension("VK_EXT_display_control") // 92
-class VkDisplayPowerInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDisplayPowerStateEXT                      powerState
-}
-
-@extension("VK_EXT_display_control") // 92
-class VkDeviceEventInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDeviceEventTypeEXT                        deviceEvent
-}
-
-@extension("VK_EXT_display_control") // 92
-class VkDisplayEventInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkDisplayEventTypeEXT                       displayEvent
-}
-
-@extension("VK_EXT_display_control") // 92
-class VkSwapchainCounterCreateInfoEXT {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkSurfaceCounterFlagsEXT                    surfaceCounters
-}
-
-@extension("VK_GOOGLE_display_timing") // 93
-class VkRefreshCycleDurationGOOGLE {
-    u64                                             refreshDuration
-}
-
-@extension("VK_GOOGLE_display_timing") // 93
-class VkPastPresentationTimingGOOGLE {
-    u32                                             presentID
-    u64                                             desiredPresentTime
-    u64                                             actualPresentTime
-    u64                                             earliestPresentTime
-    u64                                             presentMargin
-}
-
-@extension("VK_GOOGLE_display_timing") // 93
-class VkPresentTimeGOOGLE {
-    u32                                             presentID
-    u64                                             desiredPresentTime
-}
-
-@extension("VK_GOOGLE_display_timing") // 93
-class VkPresentTimesInfoGOOGLE {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             swapchainCount
-    const VkPresentTimeGOOGLE*                      pTimes
-}
-
-@extension("VK_NVX_multiview_per_view_attributes") // 98
-class VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX {
-    VkStructureType                             sType
-    void*                                       pNext
-    VkBool32                                    perViewPositionAllComponents
-}
-
-@extension("VK_NV_viewport_swizzle") // 99
-class VkViewportSwizzleNV {
-    VkViewportCoordinateSwizzleNV               x
-    VkViewportCoordinateSwizzleNV               y
-    VkViewportCoordinateSwizzleNV               z
-    VkViewportCoordinateSwizzleNV               w
-}
-
-@extension("VK_NV_viewport_swizzle") // 99
-class VkPipelineViewportSwizzleStateCreateInfoNV {
-    VkStructureType                             sType
-    const void*                                 pNext
-    VkPipelineViewportSwizzleStateCreateFlagsNV flags
-    u32                                         viewportCount
-    const VkViewportSwizzleNV*                  pViewportSwizzles
-}
-
-@extension("VK_EXT_discard_rectangles") // 100
-class VkPhysicalDeviceDiscardRectanglePropertiesEXT {
-    VkStructureType                             sType
-    void*                                       pNext
-    u32                                         maxDiscardRectangles
-}
-
-@extension("VK_EXT_discard_rectangles") // 100
-class VkPipelineDiscardRectangleStateCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkPipelineDiscardRectangleStateCreateFlagsEXT   flags
-    VkDiscardRectangleModeEXT                       discardRectangleMode
-    u32                                             discardRectangleCount
-    const VkRect2D*                                 pDiscardRectangles
-}
-
-@extension("VK_EXT_conservative_rasterization") // 102
-class VkPhysicalDeviceConservativeRasterizationPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    f32                                             primitiveOverestimationSize
-    f32                                             maxExtraPrimitiveOverestimationSize
-    f32                                             extraPrimitiveOverestimationSizeGranularity
-    VkBool32                                        primitiveUnderestimation
-    VkBool32                                        conservativePointAndLineRasterization
-    VkBool32                                        degenerateTrianglesRasterized
-    VkBool32                                        degenerateLinesRasterized
-    VkBool32                                        fullyCoveredFragmentShaderInputVariable
-    VkBool32                                        conservativeRasterizationPostDepthCoverage
-}
-
-@extension("VK_EXT_conservative_rasterization") // 102
-class VkPipelineRasterizationConservativeStateCreateInfoEXT {
-    VkStructureType                                           sType
-    const void*                                               pNext
-    VkPipelineRasterizationConservativeStateCreateFlagsEXT    flags
-    VkConservativeRasterizationModeEXT                        conservativeRasterizationMode
-    f32                                                       extraPrimitiveOverestimationSize
-}
-
-@extension("VK_EXT_hdr_metadata") // 106
-class VkXYColorEXT {
-    f32                                             x
-    f32                                             y
-}
-
-@extension("VK_EXT_hdr_metadata") // 106
-class VkHdrMetadataEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkXYColorEXT                                    displayPrimaryRed
-    VkXYColorEXT                                    displayPrimaryGreen
-    VkXYColorEXT                                    displayPrimaryBlue
-    VkXYColorEXT                                    whitePoint
-    f32                                             maxLuminance
-    f32                                             minLuminance
-    f32                                             maxContentLightLevel
-    f32                                             maxFrameAverageLightLevel
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-class VkAttachmentDescription2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkAttachmentDescriptionFlags                    flags
-    VkFormat                                        format
-    VkSampleCountFlagBits                           samples
-    VkAttachmentLoadOp                              loadOp
-    VkAttachmentStoreOp                             storeOp
-    VkAttachmentLoadOp                              stencilLoadOp
-    VkAttachmentStoreOp                             stencilStoreOp
-    VkImageLayout                                   initialLayout
-    VkImageLayout                                   finalLayout
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-class VkAttachmentReference2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             attachment
-    VkImageLayout                                   layout
-    VkImageAspectFlags                              aspectMask
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-class VkSubpassDescription2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkSubpassDescriptionFlags                       flags
-    VkPipelineBindPoint                             pipelineBindPoint
-    u32                                             viewMask
-    u32                                             inputAttachmentCount
-    const VkAttachmentReference2KHR*                pInputAttachments
-    u32                                             colorAttachmentCount
-    const VkAttachmentReference2KHR*                pColorAttachments
-    const VkAttachmentReference2KHR*                pResolveAttachments
-    const VkAttachmentReference2KHR*                pDepthStencilAttachment
-    u32                                             preserveAttachmentCount
-    const u32*                                      pPreserveAttachments
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-class VkSubpassDependency2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             srcSubpass
-    u32                                             dstSubpass
-    VkPipelineStageFlags                            srcStageMask
-    VkPipelineStageFlags                            dstStageMask
-    VkAccessFlags                                   srcAccessMask
-    VkAccessFlags                                   dstAccessMask
-    VkDependencyFlags                               dependencyFlags
-    s32                                             viewOffset
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-class VkRenderPassCreateInfo2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkRenderPassCreateFlags                         flags
-    u32                                             attachmentCount
-    const VkAttachmentDescription2KHR*              pAttachments
-    u32                                             subpassCount
-    const VkSubpassDescription2KHR*                 pSubpasses
-    u32                                             dependencyCount
-    const VkSubpassDependency2KHR*                  pDependencies
-    u32                                             correlatedViewMaskCount
-    const u32*                                      pCorrelatedViewMasks
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-class VkSubpassBeginInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkSubpassContents                               contents
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-class VkSubpassEndInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-}
-
-@extension("VK_KHR_shared_presentable_image") // 112
-class VkSharedPresentSurfaceCapabilitiesKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImageUsageFlags                               sharedPresentSupportedUsageFlags
-}
-
-@extension("VK_KHR_external_fence_capabilities") // 113
-class VkPhysicalDeviceExternalFenceInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkExternalFenceHandleTypeFlagBitsKHR            handleType
-}
-
-@extension("VK_KHR_external_fence_capabilities") // 113
-class VkExternalFencePropertiesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkExternalFenceHandleTypeFlagsKHR               exportFromImportedHandleTypes
-    VkExternalFenceHandleTypeFlagsKHR               compatibleHandleTypes
-    VkExternalFenceFeatureFlagsKHR                  externalFenceFeatures
-}
-
-@extension("VK_KHR_external_fence") // 114
-class VkExportFenceCreateInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkExternalFenceHandleTypeFlagsKHR               handleTypes
-}
-
-@extension("VK_KHR_external_fence_win32") // 115
-class VkImportFenceWin32HandleInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkFence                                         fence
-    VkFenceImportFlagsKHR                           flags
-    VkExternalFenceHandleTypeFlagBitsKHR            handleType
-    platform.HANDLE                                 handle
-    platform.LPCWSTR                                name
-}
-
-@extension("VK_KHR_external_fence_win32") // 115
-class VkExportFenceWin32HandleInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    const platform.SECURITY_ATTRIBUTES*             pAttributes
-    platform.DWORD                                  dwAccess
-    platform.LPCWSTR                                name
-}
-
-@extension("VK_KHR_external_fence_win32") // 115
-class VkFenceGetWin32HandleInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkFence                                         fence
-    VkExternalFenceHandleTypeFlagBitsKHR            handleType
-}
-
-@extension("VK_KHR_external_fence_fd") // 116
-class VkImportFenceFdInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkFence                                         fence
-    VkFenceImportFlagsKHR                           flags
-    VkExternalFenceHandleTypeFlagBitsKHR            handleType
-    int                                             fd
-}
-
-@extension("VK_KHR_external_fence_fd") // 116
-class VkFenceGetFdInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkFence                                         fence
-    VkExternalFenceHandleTypeFlagBitsKHR            handleType
-}
-
-@extension("VK_KHR_maintenance2") // 118
-class VkPhysicalDevicePointClippingPropertiesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkPointClippingBehaviorKHR                      pointClippingBehavior
-}
-
-@extension("VK_KHR_maintenance2") // 118
-class VkInputAttachmentAspectReferenceKHR {
-    u32                                             subpass
-    u32                                             inputAttachmentIndex
-    VkImageAspectFlags                              aspectMask
-}
-
-@extension("VK_KHR_maintenance2") // 118
-class VkRenderPassInputAttachmentAspectCreateInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             aspectReferenceCount
-    const VkInputAttachmentAspectReferenceKHR*      pAspectReferences
-}
-
-@extension("VK_KHR_maintenance2") // 118
-class VkImageViewUsageCreateInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImageUsageFlags                               usage
-}
-
-@extension("VK_KHR_maintenance2") // 118
-class VkPipelineTessellationDomainOriginStateCreateInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkTessellationDomainOriginKHR                   domainOrigin
-}
-
-@extension("VK_KHR_get_surface_capabilities2") // 120
-class VkPhysicalDeviceSurfaceInfo2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkSurfaceKHR                                    surface
-}
-
-@extension("VK_KHR_get_surface_capabilities2") // 120
-class VkSurfaceCapabilities2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkSurfaceCapabilitiesKHR                        surfaceCapabilities
-}
-
-@extension("VK_KHR_get_surface_capabilities2") // 120
-class VkSurfaceFormat2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkSurfaceFormatKHR                              surfaceFormat
-}
-
-@extension("VK_KHR_variable_pointers") // 121
-class VkPhysicalDeviceVariablePointerFeaturesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        variablePointersStorageBuffer
-    VkBool32                                        variablePointers
-}
-
-@extension("VK_KHR_display_properties2") // 122
-class VkDisplayProperties2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkDisplayPropertiesKHR                          displayProperties
-}
-
-@extension("VK_KHR_display_properties2") // 122
-class VkDisplayPlaneProperties2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkDisplayPlanePropertiesKHR                     displayPlaneProperties
-}
-
-@extension("VK_KHR_display_properties2") // 122
-class VkDisplayModeProperties2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkDisplayModePropertiesKHR                      displayModeProperties
-}
-
-@extension("VK_KHR_display_properties2") // 122
-class VkDisplayPlaneInfo2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkDisplayModeKHR                                mode
-    u32                                             planeIndex
-}
-
-@extension("VK_KHR_display_properties2") // 122
-class VkDisplayPlaneCapabilities2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkDisplayPlaneCapabilitiesKHR                   capabilities
-}
-
-@extension("VK_MVK_ios_surface") // 123
-class VkIOSSurfaceCreateInfoMVK {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkIOSSurfaceCreateFlagsMVK                      flags
-    const void*                                     pView
-}
-
-@extension("VK_MVK_macos_surface") // 124
-class VkMacOSSurfaceCreateInfoMVK {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkMacOSSurfaceCreateFlagsMVK                    flags
-    const void*                                     pView
-}
-
-@extension("VK_KHR_dedicated_allocation") // 128
-class VkMemoryDedicatedRequirementsKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        prefersDedicatedAllocation
-    VkBool32                                        requiresDedicatedAllocation
-}
-
-@extension("VK_KHR_dedicated_allocation") // 128
-class VkMemoryDedicatedAllocateInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImage                                         image
-    VkBuffer                                        buffer
-}
-
-@extension("VK_EXT_debug_utils") // 129
-class VkDebugUtilsObjectNameInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkObjectType                                    objectType
-    u64                                             objectHandle
-    const char*                                     pObjectName
-}
-
-@extension("VK_EXT_debug_utils") // 129
-class VkDebugUtilsObjectTagInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkObjectType                                    objectType
-    u64                                             objectHandle
-    u64                                             tagName
-    platform.size_t                                 tagSize
-    const void*                                     pTag
-}
-
-@extension("VK_EXT_debug_utils") // 129
-class VkDebugUtilsLabelEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    const char*                                     pLabelName
-    f32[4]                                          color
-}
-
-@extension("VK_EXT_debug_utils") // 129
-class VkDebugUtilsMessengerCallbackDataEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkDebugUtilsMessengerCallbackDataFlagsEXT       flags
-    const char*                                     pMessageIdName
-    s32                                             messageIdNumber
-    const char*                                     pMessage
-    u32                                             queueLabelCount
-    const VkDebugUtilsLabelEXT*                     pQueueLabels
-    u32                                             cmdBufLabelCount
-    const VkDebugUtilsLabelEXT*                     pCmdBufLabels
-    u32                                             objectCount
-    const VkDebugUtilsObjectNameInfoEXT*            pObjects
-}
-
-@extension("VK_EXT_debug_utils") // 129
-class VkDebugUtilsMessengerCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkDebugUtilsMessengerCreateFlagsEXT             flags
-    VkDebugUtilsMessageSeverityFlagsEXT             messageSeverity
-    VkDebugUtilsMessageTypeFlagsEXT                 messageTypes
-    PFN_vkDebugUtilsMessengerCallbackEXT            pfnUserCallback
-    void*                                           pUserData
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 131
-class VkAndroidHardwareBufferUsageANDROID {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u64                                             androidHardwareBufferUsage
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-class VkAndroidHardwareBufferPropertiesANDROID {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkDeviceSize                                    allocationSize
-    u32                                             memoryTypeBits
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-class VkAndroidHardwareBufferFormatPropertiesANDROID {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkFormat                                        format
-    u64                                             externalFormat
-    VkFormatFeatureFlags                            formatFeatures
-    VkComponentMapping                              samplerYcbcrConversionComponents
-    VkSamplerYcbcrModelConversion                   suggestedYcbcrModel
-    VkSamplerYcbcrRange                             suggestedYcbcrRange
-    VkChromaLocation                                suggestedXChromaOffset
-    VkChromaLocation                                suggestedYChromaOffset
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-class VkImportAndroidHardwareBufferInfoANDROID {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    platform.AHardwareBuffer*                       buffer
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-class VkMemoryGetAndroidHardwareBufferInfoANDROID {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkDeviceMemory                                  memory
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-class VkExternalFormatANDROID {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u64                                             externalFormat
-}
-
-@extension("VK_EXT_sampler_filter_minmax") // 131
-class VkSamplerReductionModeCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkSamplerReductionModeEXT                       reductionMode
-}
-
-@extension("VK_EXT_sampler_filter_minmax") // 131
-class VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        filterMinmaxSingleComponentFormats
-    VkBool32                                        filterMinmaxImageComponentMapping
-}
-
-@extension("VK_EXT_inline_uniform_block") // 139
-class VkPhysicalDeviceInlineUniformBlockFeaturesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        inlineUniformBlock
-    VkBool32                                        descriptorBindingInlineUniformBlockUpdateAfterBind
-}
-
-@extension("VK_EXT_inline_uniform_block") // 139
-class VkPhysicalDeviceInlineUniformBlockPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             maxInlineUniformBlockSize
-    u32                                             maxPerStageDescriptorInlineUniformBlocks
-    u32                                             maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks
-    u32                                             maxDescriptorSetInlineUniformBlocks
-    u32                                             maxDescriptorSetUpdateAfterBindInlineUniformBlocks
-}
-
-@extension("VK_EXT_inline_uniform_block") // 139
-class VkWriteDescriptorSetInlineUniformBlockEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             dataSize
-    const void*                                     pData
-}
-
-@extension("VK_EXT_inline_uniform_block") // 139
-class VkDescriptorPoolInlineUniformBlockCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             maxInlineUniformBlockBindings
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkSampleLocationEXT {
-    f32                                             x
-    f32                                             y
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkSampleLocationsInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkSampleCountFlagBits                           sampleLocationsPerPixel
-    VkExtent2D                                      sampleLocationGridSize
-    u32                                             sampleLocationsCount
-    const VkSampleLocationEXT*                      pSampleLocations
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkAttachmentSampleLocationsEXT {
-    u32                                             attachmentIndex
-    VkSampleLocationsInfoEXT                        sampleLocationsInfo
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkSubpassSampleLocationsEXT {
-    u32                                             subpassIndex
-    VkSampleLocationsInfoEXT                        sampleLocationsInfo
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkRenderPassSampleLocationsBeginInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             attachmentInitialSampleLocationsCount
-    const VkAttachmentSampleLocationsEXT*           pAttachmentInitialSampleLocations
-    u32                                             postSubpassSampleLocationsCount
-    const VkSubpassSampleLocationsEXT*              pPostSubpassSampleLocations
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkPipelineSampleLocationsStateCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBool32                                        sampleLocationsEnable
-    VkSampleLocationsInfoEXT                        sampleLocationsInfo
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkPhysicalDeviceSampleLocationsPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkSampleCountFlags                              sampleLocationSampleCounts
-    VkExtent2D                                      maxSampleLocationGridSize
-    f32[2]                                          sampleLocationCoordinateRange
-    u32                                             sampleLocationSubPixelBits
-    VkBool32                                        variableSampleLocations
-}
-
-@extension("VK_EXT_sample_locations") // 144
-class VkMultisamplePropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkExtent2D                                      maxSampleLocationGridSize
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-class VkBufferMemoryRequirementsInfo2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBuffer                                        buffer
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-class VkImageMemoryRequirementsInfo2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImage                                         image
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-class VkImageSparseMemoryRequirementsInfo2KHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImage                                         image
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-class VkMemoryRequirements2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkMemoryRequirements                            memoryRequirements
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-class VkSparseImageMemoryRequirements2KHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkSparseImageMemoryRequirements                 memoryRequirements
-}
-
-@extension("VK_KHR_image_format_list") // 148
-class VkImageFormatListCreateInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             viewFormatCount
-    const VkFormat*                                 pViewFormats
-}
-
-@extension("VK_EXT_blend_operation_advanced") // 149
-class VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        advancedBlendCoherentOperations
-}
-
-@extension("VK_EXT_blend_operation_advanced") // 149
-class VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             advancedBlendMaxColorAttachments
-    VkBool32                                        advancedBlendIndependentBlend
-    VkBool32                                        advancedBlendNonPremultipliedSrcColor
-    VkBool32                                        advancedBlendNonPremultipliedDstColor
-    VkBool32                                        advancedBlendCorrelatedOverlap
-    VkBool32                                        advancedBlendAllOperations
-}
-
-@extension("VK_EXT_blend_operation_advanced") // 149
-class VkPipelineColorBlendAdvancedStateCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBool32                                        srcPremultiplied
-    VkBool32                                        dstPremultiplied
-    VkBlendOverlapEXT                               blendOverlap
-}
-
-@extension("VK_NV_fragment_coverage_to_color") // 150
-class VkPipelineCoverageToColorStateCreateInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkPipelineCoverageToColorStateCreateFlagsNV     flags
-    VkBool32                                        coverageToColorEnable
-    u32                                             coverageToColorLocation
-}
-
-@extension("VK_NV_framebuffer_mixed_samples") // 153
-class VkPipelineCoverageModulationStateCreateInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkPipelineCoverageModulationStateCreateFlagsNV  flags
-    VkCoverageModulationModeNV                      coverageModulationMode
-    VkBool32                                        coverageModulationTableEnable
-    u32                                             coverageModulationTableCount
-    const f32*                                      pCoverageModulationTable
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-class VkSamplerYcbcrConversionCreateInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkFormat                                        format
-    VkSamplerYcbcrModelConversionKHR                ycbcrModel
-    VkSamplerYcbcrRangeKHR                          ycbcrRange
-    VkComponentMapping                              components
-    VkChromaLocationKHR                             xChromaOffset
-    VkChromaLocationKHR                             yChromaOffset
-    VkFilter                                        chromaFilter
-    VkBool32                                        forceExplicitReconstruction
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-class VkSamplerYcbcrConversionInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkSamplerYcbcrConversionKHR                     conversion
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-class VkBindImagePlaneMemoryInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImageAspectFlagBits                           planeAspect
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-class VkImagePlaneMemoryRequirementsInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImageAspectFlagBits                           planeAspect
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-class VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        samplerYcbcrConversion
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-class VkSamplerYcbcrConversionImageFormatPropertiesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             combinedImageSamplerDescriptorCount
-}
-
-@extension("VK_KHR_bind_memory2") // 158
-class VkBindBufferMemoryInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBuffer                                        buffer
-    VkDeviceMemory                                  memory
-    VkDeviceSize                                    memoryOffset
-}
-
-@extension("VK_KHR_bind_memory2") // 158
-class VkBindImageMemoryInfoKHR {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImage                                         image
-    VkDeviceMemory                                  memory
-    VkDeviceSize                                    memoryOffset
-}
-
-@extension("VK_EXT_image_drm_format_modifier") // 159
-class VkDrmFormatModifierPropertiesEXT {
-    u64                                             drmFormatModifier
-    u32                                             drmFormatModifierPlaneCount
-    VkFormatFeatureFlags                            drmFormatModifierTilingFeatures
-}
-
-@extension("VK_EXT_image_drm_format_modifier") // 159
-class VkDrmFormatModifierPropertiesListEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             drmFormatModifierCount
-    VkDrmFormatModifierPropertiesEXT*               pDrmFormatModifierProperties
-}
-
-@extension("VK_EXT_image_drm_format_modifier") // 159
-class VkPhysicalDeviceImageDrmFormatModifierInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u64                                             drmFormatModifier
-    VkSharingMode                                   sharingMode
-    u32                                             queueFamilyIndexCount
-    const u32*                                      pQueueFamilyIndices
-}
-
-@extension("VK_EXT_image_drm_format_modifier") // 159
-class VkImageDrmFormatModifierListCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             drmFormatModifierCount
-    const u64*                                      pDrmFormatModifiers
-}
-
-@extension("VK_EXT_image_drm_format_modifier") // 159
-class VkImageDrmFormatModifierExplicitCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u64                                             drmFormatModifier
-    u32                                             drmFormatModifierPlaneCount
-    const VkSubresourceLayout*                      pPlaneLayouts
-}
-
-@extension("VK_EXT_image_drm_format_modifier") // 159
-class VkImageDrmFormatModifierPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u64                                             drmFormatModifier
-}
-
-@extension("VK_EXT_validation_cache") // 161
-class VkValidationCacheCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkValidationCacheCreateFlagsEXT                 flags
-    platform.size_t                                 initialDataSize
-    const void*                                     pInitialData
-}
-
-@extension("VK_EXT_validation_cache") // 161
-class VkShaderModuleValidationCacheCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkValidationCacheEXT                            validationCache
-}
-
-@extension("VK_EXT_descriptor_indexing") // 162
-class VkDescriptorSetLayoutBindingFlagsCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             bindingCount
-    const VkDescriptorBindingFlagsEXT*              pBindingFlags
-}
-
-@extension("VK_EXT_descriptor_indexing") // 162
-class VkPhysicalDeviceDescriptorIndexingFeaturesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        shaderInputAttachmentArrayDynamicIndexing
-    VkBool32                                        shaderUniformTexelBufferArrayDynamicIndexing
-    VkBool32                                        shaderStorageTexelBufferArrayDynamicIndexing
-    VkBool32                                        shaderUniformBufferArrayNonUniformIndexing
-    VkBool32                                        shaderSampledImageArrayNonUniformIndexing
-    VkBool32                                        shaderStorageBufferArrayNonUniformIndexing
-    VkBool32                                        shaderStorageImageArrayNonUniformIndexing
-    VkBool32                                        shaderInputAttachmentArrayNonUniformIndexing
-    VkBool32                                        shaderUniformTexelBufferArrayNonUniformIndexing
-    VkBool32                                        shaderStorageTexelBufferArrayNonUniformIndexing
-    VkBool32                                        descriptorBindingUniformBufferUpdateAfterBind
-    VkBool32                                        descriptorBindingSampledImageUpdateAfterBind
-    VkBool32                                        descriptorBindingStorageImageUpdateAfterBind
-    VkBool32                                        descriptorBindingStorageBufferUpdateAfterBind
-    VkBool32                                        descriptorBindingUniformTexelBufferUpdateAfterBind
-    VkBool32                                        descriptorBindingStorageTexelBufferUpdateAfterBind
-    VkBool32                                        descriptorBindingUpdateUnusedWhilePending
-    VkBool32                                        descriptorBindingPartiallyBound
-    VkBool32                                        descriptorBindingVariableDescriptorCount
-    VkBool32                                        runtimeDescriptorArray
-}
-
-@extension("VK_EXT_descriptor_indexing") // 162
-class VkPhysicalDeviceDescriptorIndexingPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             maxUpdateAfterBindDescriptorsInAllPools
-    VkBool32                                        shaderUniformBufferArrayNonUniformIndexingNative
-    VkBool32                                        shaderSampledImageArrayNonUniformIndexingNative
-    VkBool32                                        shaderStorageBufferArrayNonUniformIndexingNative
-    VkBool32                                        shaderStorageImageArrayNonUniformIndexingNative
-    VkBool32                                        shaderInputAttachmentArrayNonUniformIndexingNative
-    VkBool32                                        robustBufferAccessUpdateAfterBind
-    VkBool32                                        quadDivergentImplicitLod
-    u32                                             maxPerStageDescriptorUpdateAfterBindSamplers
-    u32                                             maxPerStageDescriptorUpdateAfterBindUniformBuffers
-    u32                                             maxPerStageDescriptorUpdateAfterBindStorageBuffers
-    u32                                             maxPerStageDescriptorUpdateAfterBindSampledImages
-    u32                                             maxPerStageDescriptorUpdateAfterBindStorageImages
-    u32                                             maxPerStageDescriptorUpdateAfterBindInputAttachments
-    u32                                             maxPerStageUpdateAfterBindResources
-    u32                                             maxDescriptorSetUpdateAfterBindSamplers
-    u32                                             maxDescriptorSetUpdateAfterBindUniformBuffers
-    u32                                             maxDescriptorSetUpdateAfterBindUniformBuffersDynamic
-    u32                                             maxDescriptorSetUpdateAfterBindStorageBuffers
-    u32                                             maxDescriptorSetUpdateAfterBindStorageBuffersDynamic
-    u32                                             maxDescriptorSetUpdateAfterBindSampledImages
-    u32                                             maxDescriptorSetUpdateAfterBindStorageImages
-    u32                                             maxDescriptorSetUpdateAfterBindInputAttachments
-}
-
-@extension("VK_EXT_descriptor_indexing") // 162
-class VkDescriptorSetVariableDescriptorCountAllocateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             descriptorSetCount
-    const u32*                                      pDescriptorCounts
-}
-
-@extension("VK_EXT_descriptor_indexing") // 162
-class VkDescriptorSetVariableDescriptorCountLayoutSupportEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             maxVariableDescriptorCount
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-class VkShadingRatePaletteNV {
-    u32                                             shadingRatePaletteEntryCount
-    const VkShadingRatePaletteEntryNV*              pShadingRatePaletteEntries
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-class VkPipelineViewportShadingRateImageStateCreateInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBool32                                        shadingRateImageEnable
-    u32                                             viewportCount
-    const VkShadingRatePaletteNV*                   pShadingRatePalettes
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-class VkPhysicalDeviceShadingRateImageFeaturesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        shadingRateImage
-    VkBool32                                        shadingRateCoarseSampleOrder
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-class VkPhysicalDeviceShadingRateImagePropertiesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkExtent2D                                      shadingRateTexelSize
-    u32                                             shadingRatePaletteSize
-    u32                                             shadingRateMaxCoarseSamples
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-class VkCoarseSampleLocationNV {
-    u32                                             pixelX
-    u32                                             pixelY
-    u32                                             sample
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-class VkCoarseSampleOrderCustomNV {
-    VkShadingRatePaletteEntryNV                     shadingRate
-    u32                                             sampleCount
-    u32                                             sampleLocationCount
-    const VkCoarseSampleLocationNV*                 pSampleLocations
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-class VkPipelineViewportCoarseSampleOrderStateCreateInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkCoarseSampleOrderTypeNV                       sampleOrderType
-    u32                                             customSampleOrderCount
-    const VkCoarseSampleOrderCustomNV*              pCustomSampleOrders
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkRayTracingShaderGroupCreateInfoNV {
-    VkStructureType                  sType
-    const void*                      pNext
-    VkRayTracingShaderGroupTypeNV    type
-    u32                              generalShader
-    u32                              closestHitShader
-    u32                              anyHitShader
-    u32                              intersectionShader
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkRayTracingPipelineCreateInfoNV {
-    VkStructureType                               sType
-    const void*                                   pNext
-    VkPipelineCreateFlags                         flags
-    u32                                           stageCount
-    const VkPipelineShaderStageCreateInfo*        pStages
-    u32                                           groupCount
-    const VkRayTracingShaderGroupCreateInfoNV*    pGroups
-    u32                                           maxRecursionDepth
-    VkPipelineLayout                              layout
-    VkPipeline                                    basePipelineHandle
-    s32                                           basePipelineIndex
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkGeometryTrianglesNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBuffer                                        vertexData
-    VkDeviceSize                                    vertexOffset
-    u32                                             vertexCount
-    VkDeviceSize                                    vertexStride
-    VkFormat                                        vertexFormat
-    VkBuffer                                        indexData
-    VkDeviceSize                                    indexOffset
-    u32                                             indexCount
-    VkIndexType                                     indexType
-    VkBuffer                                        transformData
-    VkDeviceSize                                    transformOffset
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkGeometryAABBNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBuffer                                        aabbData
-    u32                                             numAABBs
-    u32                                             stride
-    VkDeviceSize                                    offset
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkGeometryDataNV {
-    VkGeometryTrianglesNV                           triangles
-    VkGeometryAABBNV                                aabbs
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkGeometryNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkGeometryTypeNV                                geometryType
-    VkGeometryDataNV                                geometry
-    VkGeometryFlagsNV                               flags
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkAccelerationStructureInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkAccelerationStructureTypeNV                   type
-    VkBuildAccelerationStructureFlagsNV             flags
-    u32                                             instanceCount
-    u32                                             geometryCount
-    const VkGeometryNV*                             pGeometries
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkAccelerationStructureCreateInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkDeviceSize                                    compactedSize
-    VkAccelerationStructureInfoNV                   info
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkBindAccelerationStructureMemoryInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkAccelerationStructureNV                       accelerationStructure
-    VkDeviceMemory                                  memory
-    VkDeviceSize                                    memoryOffset
-    u32                                             deviceIndexCount
-    const u32*                                      pDeviceIndices
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkDescriptorAccelerationStructureInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             accelerationStructureCount
-    const VkAccelerationStructureNV*                pAccelerationStructures
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkAccelerationStructureMemoryRequirementsInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkAccelerationStructureMemoryRequirementsTypeNV type
-    VkAccelerationStructureNV                       accelerationStructure
-}
-
-@extension("VK_NV_ray_tracing") // 166
-class VkPhysicalDeviceRaytracingPropertiesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             shaderGroupHandleSize
-    u32                                             maxRecursionDepth
-    u32                                             maxShaderGroupStride
-    u32                                             shaderGroupBaseAlignment
-    u64                                             maxGeometryCount
-    u64                                             maxInstanceCount
-    u64                                             maxTriangleCount
-    u32                                             maxDescriptorSetAccelerationStructures
-}
-
-@extension("VK_NV_representative_fragment_test") // 167
-class VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        representativeFragmentTest
-}
-
-@extension("VK_NV_representative_fragment_test") // 167
-class VkPipelineRepresentativeFragmentTestStateCreateInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkBool32                                        representativeFragmentTestEnable
-}
-
-@extension("VK_KHR_maintenance3") // 169
-class VkPhysicalDeviceMaintenance3PropertiesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             maxPerSetDescriptors
-    VkDeviceSize                                    maxMemoryAllocationSize
-}
-
-@extension("VK_KHR_maintenance3") // 169
-class VkDescriptorSetLayoutSupportKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        supported
-}
-
-@extension("VK_EXT_global_priority") // 175
-class VkDeviceQueueGlobalPriorityCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkQueueGlobalPriorityEXT                        globalPriority
-}
-
-@extension("VK_KHR_8bit_storage") // 178
-class VkPhysicalDevice8BitStorageFeaturesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        storageBuffer8BitAccess
-    VkBool32                                        uniformAndStorageBuffer8BitAccess
-    VkBool32                                        storagePushConstant8
-}
-
-@extension("VK_EXT_external_memory_host") // 179
-class VkImportMemoryHostPointerInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkExternalMemoryHandleTypeFlagBits              handleType
-    void*                                           pHostPointer
-}
-
-@extension("VK_EXT_external_memory_host") // 179
-class VkMemoryHostPointerPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             memoryTypeBits
-}
-
-@extension("VK_EXT_external_memory_host") // 179
-class VkPhysicalDeviceExternalMemoryHostPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkDeviceSize                                    minImportedHostPointerAlignment
-}
-
-@extension("VK_KHR_shader_atomic_int64") // 181
-class VkPhysicalDeviceShaderAtomicInt64FeaturesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        shaderBufferInt64Atomics
-    VkBool32                                        shaderSharedInt64Atomics
-}
-
-@extension("VK_EXT_calibrated_timestamps") // 185
-class VkCalibratedTimestampInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkTimeDomainEXT                                 timeDomain
-}
-
-@extension("VK_AMD_shader_core_properties") // 186
-class VkPhysicalDeviceShaderCorePropertiesAMD {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             shaderEngineCount
-    u32                                             shaderArraysPerEngineCount
-    u32                                             computeUnitsPerShaderArray
-    u32                                             simdPerComputeUnit
-    u32                                             wavefrontsPerSimd
-    u32                                             wavefrontSize
-    u32                                             sgprsPerSimd
-    u32                                             minSgprAllocation
-    u32                                             maxSgprAllocation
-    u32                                             sgprAllocationGranularity
-    u32                                             vgprsPerSimd
-    u32                                             minVgprAllocation
-    u32                                             maxVgprAllocation
-    u32                                             vgprAllocationGranularity
-}
-
-@extension("VK_AMD_memory_overallocation_behavior") // 190
-class VkDeviceMemoryOverallocationCreateInfoAMD {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkMemoryOverallocationBehaviorAMD               overallocationBehavior
-}
-
-@extension("VK_EXT_vertex_attribute_divisor") // 191
-class VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             maxVertexAttribDivisor
-}
-
-@extension("VK_EXT_vertex_attribute_divisor") // 191
-class VkVertexInputBindingDivisorDescriptionEXT {
-    u32                                             binding
-    u32                                             divisor
-}
-
-@extension("VK_EXT_vertex_attribute_divisor") // 191
-class VkPipelineVertexInputDivisorStateCreateInfoEXT {
-    VkStructureType                                     sType
-    const void*                                         pNext
-    u32                                                 vertexBindingDivisorCount
-    const VkVertexInputBindingDivisorDescriptionEXT*    pVertexBindingDivisors
-}
-
-@extension("VK_EXT_vertex_attribute_divisor") // 191
-class VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        vertexAttributeInstanceRateDivisor
-    VkBool32                                        vertexAttributeInstanceRateZeroDivisor
-}
-
-@extension("VK_KHR_driver_properties") // 197
-class VkConformanceVersionKHR {
-    u8                                              major
-    u8                                              minor
-    u8                                              subminor
-    u8                                              patch
-}
-
-@extension("VK_KHR_driver_properties") // 197
-class VkPhysicalDeviceDriverPropertiesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkDriverIdKHR                                   driverID
-    char[VK_MAX_DRIVER_NAME_SIZE_KHR]               driverName
-    char[VK_MAX_DRIVER_INFO_SIZE_KHR]               driverInfo
-    VkConformanceVersionKHR                         conformanceVersion
-}
-
-@extension("VK_KHR_shader_float_controls") // 198
-class VkPhysicalDeviceFloatControlsPropertiesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        separateDenormSettings
-    VkBool32                                        separateRoundingModeSettings
-    VkBool32                                        shaderSignedZeroInfNanPreserveFloat16
-    VkBool32                                        shaderSignedZeroInfNanPreserveFloat32
-    VkBool32                                        shaderSignedZeroInfNanPreserveFloat64
-    VkBool32                                        shaderDenormPreserveFloat16
-    VkBool32                                        shaderDenormPreserveFloat32
-    VkBool32                                        shaderDenormPreserveFloat64
-    VkBool32                                        shaderDenormFlushToZeroFloat16
-    VkBool32                                        shaderDenormFlushToZeroFloat32
-    VkBool32                                        shaderDenormFlushToZeroFloat64
-    VkBool32                                        shaderRoundingModeRTEFloat16
-    VkBool32                                        shaderRoundingModeRTEFloat32
-    VkBool32                                        shaderRoundingModeRTEFloat64
-    VkBool32                                        shaderRoundingModeRTZFloat16
-    VkBool32                                        shaderRoundingModeRTZFloat32
-    VkBool32                                        shaderRoundingModeRTZFloat64
-}
-
-@extension("VK_NV_compute_shader_derivatives") // 202
-class VkPhysicalDeviceComputeShaderDerivativesFeaturesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        computeDerivativeGroupQuads
-    VkBool32                                        computeDerivativeGroupLinear
-}
-
-@extension("VK_NV_mesh_shader") // 203
-class VkPhysicalDeviceMeshShaderFeaturesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        taskShader
-    VkBool32                                        meshShader
-}
-
-@extension("VK_NV_mesh_shader") // 203
-class VkPhysicalDeviceMeshShaderPropertiesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             maxDrawMeshTasksCount
-    u32                                             maxTaskWorkGroupInvocations
-    u32[3]                                          maxTaskWorkGroupSize
-    u32                                             maxTaskTotalMemorySize
-    u32                                             maxTaskOutputCount
-    u32                                             maxMeshWorkGroupInvocations
-    u32[3]                                          maxMeshWorkGroupSize
-    u32                                             maxMeshTotalMemorySize
-    u32                                             maxMeshOutputVertices
-    u32                                             maxMeshOutputPrimitives
-    u32                                             maxMeshMultiviewViewCount
-    u32                                             meshOutputPerVertexGranularity
-    u32                                             meshOutputPerPrimitiveGranularity
-}
-
-@extension("VK_NV_mesh_shader") // 203
-class VkDrawMeshTasksIndirectCommandNV {
-    u32                                             taskCount
-    u32                                             firstTask
-}
-
-@extension("VK_NV_fragment_shader_barycentric") // 204
-class VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        fragmentShaderBarycentric
-}
-
-@extension("VK_NV_shader_image_footprint") // 205
-class VkPhysicalDeviceShaderImageFootprintFeaturesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        imageFootprint
-}
-
-@extension("VK_NV_scissor_exclusive") // 206
-class VkPipelineViewportExclusiveScissorStateCreateInfoNV {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    u32                                             exclusiveScissorCount
-    const VkRect2D*                                 pExclusiveScissors
-}
-
-@extension("VK_NV_scissor_exclusive") // 206
-class VkPhysicalDeviceExclusiveScissorFeaturesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        exclusiveScissor
-}
-
-@extension("VK_NV_device_diagnostic_checkpoints") // 207
-class VkQueueFamilyCheckpointPropertiesNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkPipelineStageFlags                            checkpointExecutionStageMask
-}
-
-@extension("VK_NV_device_diagnostic_checkpoints") // 207
-class VkCheckpointDataNV {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkPipelineStageFlagBits                         stage
-    void*                                           pCheckpointMarker
-}
-
-@extension("VK_KHR_vulkan_memory_model") // 212
-class VkPhysicalDeviceVulkanMemoryModelFeaturesKHR {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        vulkanMemoryModel
-    VkBool32                                        vulkanMemoryModelDeviceScope
-}
-
-@extension("VK_EXT_pci_bus_info") // 213
-class VkPhysicalDevicePCIBusInfoPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    u32                                             pciDomain
-    u32                                             pciBus
-    u32                                             pciDevice
-    u32                                             pciFunction
-}
-
-@extension("VK_FUCHSIA_imagepipe_surface") // 215
-class VkImagePipeSurfaceCreateInfoFUCHSIA {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImagePipeSurfaceCreateFlagsFUCHSIA            flags
-    platform.zx_handle_t                            imagePipeHandle
-}
-
-@extension("VK_EXT_fragment_density_map") // 219
-class VkPhysicalDeviceFragmentDensityMapFeaturesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        fragmentDensityMap
-    VkBool32                                        fragmentDensityMapDynamic
-    VkBool32                                        fragmentDensityMapNonSubsampledImages
-}
-
-@extension("VK_EXT_fragment_density_map") // 219
-class VkPhysicalDeviceFragmentDensityMapPropertiesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkExtent2D                                      minFragmentDensityTexelSize
-    VkExtent2D                                      maxFragmentDensityTexelSize
-    VkBool32                                        fragmentDensityInvocations
-}
-
-@extension("VK_EXT_fragment_density_map") // 219
-class VkRenderPassFragmentDensityMapCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkAttachmentReference                           fragmentDensityMapAttachment
-}
-
-@extension("VK_EXT_scalar_block_layout") // 222
-class VkPhysicalDeviceScalarBlockLayoutFeaturesEXT {
-    VkStructureType                                 sType
-    void*                                           pNext
-    VkBool32                                        scalarBlockLayout
-}
-
-@extension("VK_EXT_separate_stencil_usage") // 247
-class VkImageStencilUsageCreateInfoEXT {
-    VkStructureType                                 sType
-    const void*                                     pNext
-    VkImageUsageFlags                               stencilUsage
-}
-
-
-////////////////
-//  Commands  //
-////////////////
-
-// Function pointers. TODO: add support for function pointers.
-
-@external type void* PFN_vkVoidFunction
-@pfn cmd void vkVoidFunction() {
-}
-
-@external type void* PFN_vkAllocationFunction
-@pfn cmd void* vkAllocationFunction(
-        void*                                       pUserData,
-        platform.size_t                             size,
-        platform.size_t                             alignment,
-        VkSystemAllocationScope                     allocationScope) {
-    return ?
-}
-
-@external type void* PFN_vkReallocationFunction
-@pfn cmd void* vkReallocationFunction(
-        void*                                       pUserData,
-        void*                                       pOriginal,
-        platform.size_t                             size,
-        platform.size_t                             alignment,
-        VkSystemAllocationScope                     allocationScope) {
-    return ?
-}
-
-@external type void* PFN_vkFreeFunction
-@pfn cmd void vkFreeFunction(
-        void*                                       pUserData,
-        void*                                       pMemory) {
-}
-
-@external type void* PFN_vkInternalAllocationNotification
-@pfn cmd void vkInternalAllocationNotification(
-        void*                                       pUserData,
-        platform.size_t                             size,
-        VkInternalAllocationType                    allocationType,
-        VkSystemAllocationScope                     allocationScope) {
-}
-
-@external type void* PFN_vkInternalFreeNotification
-@pfn cmd void vkInternalFreeNotification(
-        void*                                       pUserData,
-        platform.size_t                             size,
-        VkInternalAllocationType                    allocationType,
-        VkSystemAllocationScope                     allocationScope) {
-}
-
-// Global functions
-
-@threadSafety("system")
-cmd VkResult vkCreateInstance(
-        const VkInstanceCreateInfo*                 pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkInstance*                                 pInstance) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO)
-
-    instance := ?
-    pInstance[0] = instance
-    State.Instances[instance] = new!InstanceObject()
-
-    layers := pCreateInfo.ppEnabledLayerNames[0:pCreateInfo.enabledLayerCount]
-    extensions := pCreateInfo.ppEnabledExtensionNames[0:pCreateInfo.enabledExtensionCount]
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyInstance(
-        VkInstance                                  instance,
-        const VkAllocationCallbacks*                pAllocator) {
-    instanceObject := GetInstance(instance)
-
-    State.Instances[instance] = null
-}
-
-@threadSafety("system")
-cmd VkResult vkEnumeratePhysicalDevices(
-        VkInstance                                  instance,
-        u32*                                        pPhysicalDeviceCount,
-        VkPhysicalDevice*                           pPhysicalDevices) {
-    instanceObject := GetInstance(instance)
-
-    physicalDeviceCount := as!u32(?)
-    pPhysicalDeviceCount[0] = physicalDeviceCount
-    physicalDevices := pPhysicalDevices[0:physicalDeviceCount]
-
-    for i in (0 .. physicalDeviceCount) {
-        physicalDevice := ?
-        physicalDevices[i] = physicalDevice
-        if !(physicalDevice in State.PhysicalDevices) {
-            State.PhysicalDevices[physicalDevice] = new!PhysicalDeviceObject(instance: instance)
-        }
-    }
-
-    return ?
-}
-
-cmd PFN_vkVoidFunction vkGetDeviceProcAddr(
-        VkDevice                                    device,
-        const char*                                 pName) {
-    if device != NULL_HANDLE {
-        device := GetDevice(device)
-    }
-
-    return ?
-}
-
-cmd PFN_vkVoidFunction vkGetInstanceProcAddr(
-        VkInstance                                  instance,
-        const char*                                 pName) {
-    if instance != NULL_HANDLE {
-        instanceObject := GetInstance(instance)
-    }
-
-    return ?
-}
-
-cmd void vkGetPhysicalDeviceProperties(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceProperties*                 pProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    properties := ?
-    pProperties[0] = properties
-}
-
-cmd void vkGetPhysicalDeviceQueueFamilyProperties(
-        VkPhysicalDevice                            physicalDevice,
-        u32*                                        pQueueFamilyPropertyCount,
-        VkQueueFamilyProperties*                    pQueueFamilyProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    // TODO: Figure out how to express fetch-count-or-properties
-    // This version fails 'apic validate' with 'fence not allowed in
-    // *semantic.Branch'. Other attempts have failed with the same or other
-    // errors.
-    // if pQueueFamilyProperties != null {
-    //     queuesProperties := pQueueFamilyProperties[0:pCount[0]]
-    //     for i in (0 .. pCount[0]) {
-    //         queueProperties := as!VkQueueFamilyProperties(?)
-    //         queuesProperties[i] = queueProperties
-    //    }
-    // } else {
-    //     count := ?
-    //     pCount[0] = count
-    // }
-}
-
-cmd void vkGetPhysicalDeviceMemoryProperties(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceMemoryProperties*           pMemoryProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    memoryProperties := ?
-    pMemoryProperties[0] = memoryProperties
-}
-
-cmd void vkGetPhysicalDeviceFeatures(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceFeatures*                   pFeatures) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    features := ?
-    pFeatures[0] = features
-}
-
-cmd void vkGetPhysicalDeviceFormatProperties(
-        VkPhysicalDevice                            physicalDevice,
-        VkFormat                                    format,
-        VkFormatProperties*                         pFormatProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    formatProperties := ?
-    pFormatProperties[0] = formatProperties
-}
-
-cmd VkResult vkGetPhysicalDeviceImageFormatProperties(
-        VkPhysicalDevice                            physicalDevice,
-        VkFormat                                    format,
-        VkImageType                                 type,
-        VkImageTiling                               tiling,
-        VkImageUsageFlags                           usage,
-        VkImageCreateFlags                          flags,
-        VkImageFormatProperties*                    pImageFormatProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    imageFormatProperties := ?
-    pImageFormatProperties[0] = imageFormatProperties
-
-    return ?
-}
-
-
-// Device functions
-
-@threadSafety("system")
-cmd VkResult vkCreateDevice(
-        VkPhysicalDevice                            physicalDevice,
-        const VkDeviceCreateInfo*                   pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDevice*                                   pDevice) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO)
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    device := ?
-    pDevice[0] = device
-    State.Devices[device] = new!DeviceObject(physicalDevice: physicalDevice)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyDevice(
-        VkDevice                                    device,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-
-    State.Devices[device] = null
-}
-
-
-// Extension discovery functions
-
-cmd VkResult vkEnumerateInstanceLayerProperties(
-        u32*                                        pPropertyCount,
-        VkLayerProperties*                          pProperties) {
-    count := as!u32(?)
-    pPropertyCount[0] = count
-
-    properties := pProperties[0:count]
-    for i in (0 .. count) {
-        property := ?
-        properties[i] = property
-    }
-
-    return ?
-}
-
-cmd VkResult vkEnumerateInstanceExtensionProperties(
-        const char*                                 pLayerName,
-        u32*                                        pPropertyCount,
-        VkExtensionProperties*                      pProperties) {
-    count := as!u32(?)
-    pPropertyCount[0] = count
-
-    properties := pProperties[0:count]
-    for i in (0 .. count) {
-        property := ?
-        properties[i] = property
-    }
-
-    return ?
-}
-
-cmd VkResult vkEnumerateDeviceLayerProperties(
-        VkPhysicalDevice                            physicalDevice,
-        u32*                                        pPropertyCount,
-        VkLayerProperties*                          pProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    count := as!u32(?)
-    pPropertyCount[0] = count
-
-    properties := pProperties[0:count]
-    for i in (0 .. count) {
-        property := ?
-        properties[i] = property
-    }
-
-    return ?
-}
-
-cmd VkResult vkEnumerateDeviceExtensionProperties(
-        VkPhysicalDevice                            physicalDevice,
-        const char*                                 pLayerName,
-        u32*                                        pPropertyCount,
-        VkExtensionProperties*                      pProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    count := as!u32(?)
-    pPropertyCount[0] = count
-
-    properties := pProperties[0:count]
-    for i in (0 .. count) {
-        property := ?
-        properties[i] = property
-    }
-
-    return ?
-}
-
-
-// Queue functions
-
-@threadSafety("system")
-cmd void vkGetDeviceQueue(
-        VkDevice                                    device,
-        u32                                         queueFamilyIndex,
-        u32                                         queueIndex,
-        VkQueue*                                    pQueue) {
-    deviceObject := GetDevice(device)
-
-    queue := ?
-    pQueue[0] = queue
-
-    if !(queue in State.Queues) {
-        State.Queues[queue] = new!QueueObject(device: device)
-    }
-}
-
-@threadSafety("app")
-cmd VkResult vkQueueSubmit(
-        VkQueue                                     queue,
-        u32                                         submitCount,
-        const VkSubmitInfo*                         pSubmits,
-        VkFence                                     fence) {
-    queueObject := GetQueue(queue)
-
-    if fence != NULL_HANDLE {
-        fenceObject := GetFence(fence)
-        assert(fenceObject.device == queueObject.device)
-    }
-
-    // commandBuffers := pcommandBuffers[0:commandBufferCount]
-    // for i in (0 .. commandBufferCount) {
-    //    commandBuffer := commandBuffers[i]
-    //    commandBufferObject := GetCommandBuffer(commandBuffer)
-    //    assert(commandBufferObject.device == queueObject.device)
-    //
-    //    validate("QueueCheck", commandBufferObject.queueFlags in queueObject.flags,
-    //        "vkQueueSubmit: enqueued commandBuffer requires missing queue capabilities.")
-    // }
-
-    return ?
-}
-
-@threadSafety("system")
-cmd VkResult vkQueueWaitIdle(
-        VkQueue                                     queue) {
-    queueObject := GetQueue(queue)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd VkResult vkDeviceWaitIdle(
-        VkDevice                                    device) {
-    deviceObject := GetDevice(device)
-
-    return ?
-}
-
-
-// Memory functions
-
-@threadSafety("system")
-cmd VkResult vkAllocateMemory(
-        VkDevice                                    device,
-        const VkMemoryAllocateInfo*                 pAllocateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDeviceMemory*                             pMemory) {
-    assert(pAllocateInfo.sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO)
-    deviceObject := GetDevice(device)
-
-    memory := ?
-    pMemory[0] = memory
-    State.DeviceMemories[memory] = new!DeviceMemoryObject(
-        device: device,
-        allocationSize: pAllocateInfo[0].allocationSize)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkFreeMemory(
-        VkDevice                                    device,
-        VkDeviceMemory                              memory,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    memoryObject := GetDeviceMemory(memory)
-    assert(memoryObject.device == device)
-
-    // Check that no objects are still bound before freeing.
-    validate("MemoryCheck", len(memoryObject.boundObjects) == 0,
-        "vkFreeMemory: objects still bound")
-    validate("MemoryCheck", len(memoryObject.boundCommandBuffers) == 0,
-        "vkFreeMemory: commandBuffers still bound")
-    State.DeviceMemories[memory] = null
-}
-
-@threadSafety("app")
-cmd VkResult vkMapMemory(
-        VkDevice                                    device,
-        VkDeviceMemory                              memory,
-        VkDeviceSize                                offset,
-        VkDeviceSize                                size,
-        VkMemoryMapFlags                            flags,
-        void**                                      ppData) {
-    deviceObject := GetDevice(device)
-    memoryObject := GetDeviceMemory(memory)
-    assert(memoryObject.device == device)
-
-    assert(flags == as!VkMemoryMapFlags(0))
-    assert((offset + size) <= memoryObject.allocationSize)
-
-    return ?
-}
-
-@threadSafety("app")
-cmd void vkUnmapMemory(
-        VkDevice                                    device,
-        VkDeviceMemory                              memory) {
-    deviceObject := GetDevice(device)
-    memoryObject := GetDeviceMemory(memory)
-    assert(memoryObject.device == device)
-}
-
-cmd VkResult vkFlushMappedMemoryRanges(
-        VkDevice                                    device,
-        u32                                         memoryRangeCount
-        const VkMappedMemoryRange*                  pMemoryRanges) {
-    deviceObject := GetDevice(device)
-
-    memoryRanges := pMemoryRanges[0:memoryRangeCount]
-    for i in (0 .. memoryRangeCount) {
-        memoryRange := memoryRanges[i]
-        memoryObject := GetDeviceMemory(memoryRange.memory)
-        assert(memoryObject.device == device)
-        assert((memoryRange.offset + memoryRange.size) <= memoryObject.allocationSize)
-    }
-
-    return ?
-}
-
-cmd VkResult vkInvalidateMappedMemoryRanges(
-        VkDevice                                    device,
-        u32                                         memoryRangeCount,
-        const VkMappedMemoryRange*                  pMemoryRanges) {
-    deviceObject := GetDevice(device)
-
-    memoryRanges := pMemoryRanges[0:memoryRangeCount]
-    for i in (0 .. memoryRangeCount) {
-        memoryRange := memoryRanges[i]
-        memoryObject := GetDeviceMemory(memoryRange.memory)
-        assert(memoryObject.device == device)
-        assert((memoryRange.offset + memoryRange.size) <= memoryObject.allocationSize)
-    }
-
-    return ?
-}
-
-
-// Memory management API functions
-
-cmd void vkGetDeviceMemoryCommitment(
-        VkDevice                                    device,
-        VkDeviceMemory                              memory,
-        VkDeviceSize*                               pCommittedMemoryInBytes) {
-    deviceObject := GetDevice(device)
-
-    if memory != NULL_HANDLE {
-        memoryObject := GetDeviceMemory(memory)
-        assert(memoryObject.device == device)
-    }
-
-    committedMemoryInBytes := ?
-    pCommittedMemoryInBytes[0] = committedMemoryInBytes
-}
-
-cmd void vkGetBufferMemoryRequirements(
-        VkDevice                                    device,
-        VkBuffer                                    buffer,
-        VkMemoryRequirements*                       pMemoryRequirements) {
-    deviceObject := GetDevice(device)
-    bufferObject := GetBuffer(buffer)
-    assert(bufferObject.device == device)
-}
-
-cmd VkResult vkBindBufferMemory(
-        VkDevice                                    device,
-        VkBuffer                                    buffer,
-        VkDeviceMemory                              memory,
-        VkDeviceSize                                memoryOffset) {
-    deviceObject := GetDevice(device)
-    bufferObject := GetBuffer(buffer)
-    assert(bufferObject.device == device)
-
-    // Unbind buffer from previous memory object, if not VK_NULL_HANDLE.
-    if bufferObject.memory != NULL_HANDLE {
-        memoryObject := GetDeviceMemory(bufferObject.memory)
-        memoryObject.boundObjects[as!u64(buffer)] = null
-    }
-
-    // Bind buffer to given memory object, if not VK_NULL_HANDLE.
-    if memory != NULL_HANDLE {
-        memoryObject := GetDeviceMemory(memory)
-        assert(memoryObject.device == device)
-        memoryObject.boundObjects[as!u64(buffer)] = memoryOffset
-    }
-    bufferObject.memory = memory
-    bufferObject.memoryOffset = memoryOffset
-
-    return ?
-}
-
-cmd void vkGetImageMemoryRequirements(
-        VkDevice                                    device,
-        VkImage                                     image,
-        VkMemoryRequirements*                       pMemoryRequirements) {
-    deviceObject := GetDevice(device)
-    imageObject := GetImage(image)
-    assert(imageObject.device == device)
-}
-
-cmd VkResult vkBindImageMemory(
-        VkDevice                                    device,
-        VkImage                                     image,
-        VkDeviceMemory                              memory,
-        VkDeviceSize                                memoryOffset) {
-    deviceObject := GetDevice(device)
-    imageObject := GetImage(image)
-    assert(imageObject.device == device)
-
-    // Unbind image from previous memory object, if not VK_NULL_HANDLE.
-    if imageObject.memory != NULL_HANDLE {
-        memoryObject := GetDeviceMemory(imageObject.memory)
-        memoryObject.boundObjects[as!u64(image)] = null
-    }
-
-    // Bind image to given memory object, if not VK_NULL_HANDLE.
-    if memory != NULL_HANDLE {
-        memoryObject := GetDeviceMemory(memory)
-        assert(memoryObject.device == device)
-        memoryObject.boundObjects[as!u64(image)] = memoryOffset
-    }
-    imageObject.memory = memory
-    imageObject.memoryOffset = memoryOffset
-
-    return ?
-}
-
-cmd void vkGetImageSparseMemoryRequirements(
-        VkDevice                                    device,
-        VkImage                                     image,
-        u32*                                        pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements*            pSparseMemoryRequirements) {
-    deviceObject := GetDevice(device)
-    imageObject := GetImage(image)
-    assert(imageObject.device == device)
-}
-
-cmd void vkGetPhysicalDeviceSparseImageFormatProperties(
-        VkPhysicalDevice                            physicalDevice,
-        VkFormat                                    format,
-        VkImageType                                 type,
-        VkSampleCountFlagBits                       samples,
-        VkImageUsageFlags                           usage,
-        VkImageTiling                               tiling,
-        u32*                                        pPropertyCount,
-        VkSparseImageFormatProperties*              pProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-}
-
-cmd VkResult vkQueueBindSparse(
-        VkQueue                                     queue,
-        u32                                         bindInfoCount,
-        const VkBindSparseInfo*                     pBindInfo,
-        VkFence                                     fence) {
-    queueObject := GetQueue(queue)
-
-    return ?
-}
-
-
-// Fence functions
-
-@threadSafety("system")
-cmd VkResult vkCreateFence(
-        VkDevice                                    device,
-        const VkFenceCreateInfo*                    pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkFence*                                    pFence) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_FENCE_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    fence := ?
-    pFence[0] = fence
-    State.Fences[fence] = new!FenceObject(
-        device: device, signaled: (pCreateInfo.flags == as!VkFenceCreateFlags(VK_FENCE_CREATE_SIGNALED_BIT)))
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyFence(
-        VkDevice                                    device,
-        VkFence                                     fence,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    fenceObject := GetFence(fence)
-    assert(fenceObject.device == device)
-
-    State.Fences[fence] = null
-}
-
-@threadSafety("system")
-cmd VkResult vkResetFences(
-        VkDevice                                    device,
-        u32                                         fenceCount,
-        const VkFence*                              pFences) {
-    deviceObject := GetDevice(device)
-
-    fences := pFences[0:fenceCount]
-    for i in (0 .. fenceCount) {
-        fence := fences[i]
-        fenceObject := GetFence(fence)
-        assert(fenceObject.device == device)
-        fenceObject.signaled = false
-    }
-
-    return ?
-}
-
-@threadSafety("system")
-cmd VkResult vkGetFenceStatus(
-        VkDevice                                    device,
-        VkFence                                     fence) {
-    deviceObject := GetDevice(device)
-    fenceObject := GetFence(fence)
-    assert(fenceObject.device == device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd VkResult vkWaitForFences(
-        VkDevice                                    device,
-        u32                                         fenceCount,
-        const VkFence*                              pFences,
-        VkBool32                                    waitAll,
-        u64                                         timeout) {  /// timeout in nanoseconds
-    deviceObject := GetDevice(device)
-
-    fences := pFences[0:fenceCount]
-    for i in (0 .. fenceCount) {
-        fence := fences[i]
-        fenceObject := GetFence(fence)
-        assert(fenceObject.device == device)
-    }
-
-    return ?
-}
-
-
-// Queue semaphore functions
-
-@threadSafety("system")
-cmd VkResult vkCreateSemaphore(
-        VkDevice                                    device,
-        const VkSemaphoreCreateInfo*                pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkSemaphore*                                pSemaphore) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    semaphore := ?
-    pSemaphore[0] = semaphore
-    State.Semaphores[semaphore] = new!SemaphoreObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroySemaphore(
-        VkDevice                                    device,
-        VkSemaphore                                 semaphore,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    semaphoreObject := GetSemaphore(semaphore)
-    assert(semaphoreObject.device == device)
-
-    State.Semaphores[semaphore] = null
-}
-
-
-// Event functions
-
-@threadSafety("system")
-cmd VkResult vkCreateEvent(
-        VkDevice                                    device,
-        const VkEventCreateInfo*                    pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkEvent*                                    pEvent) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_EVENT_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    event := ?
-    pEvent[0] = event
-    State.Events[event] = new!EventObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyEvent(
-        VkDevice                                    device,
-        VkEvent                                     event,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    eventObject := GetEvent(event)
-    assert(eventObject.device == device)
-
-    State.Events[event] = null
-}
-
-@threadSafety("system")
-cmd VkResult vkGetEventStatus(
-        VkDevice                                    device,
-        VkEvent                                     event) {
-    deviceObject := GetDevice(device)
-    eventObject := GetEvent(event)
-    assert(eventObject.device == device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd VkResult vkSetEvent(
-        VkDevice                                    device,
-        VkEvent                                     event) {
-    deviceObject := GetDevice(device)
-    eventObject := GetEvent(event)
-    assert(eventObject.device == device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd VkResult vkResetEvent(
-        VkDevice                                    device,
-        VkEvent                                     event) {
-    deviceObject := GetDevice(device)
-    eventObject := GetEvent(event)
-    assert(eventObject.device == device)
-
-    return ?
-}
-
-
-// Query functions
-
-@threadSafety("system")
-cmd VkResult vkCreateQueryPool(
-        VkDevice                                    device,
-        const VkQueryPoolCreateInfo*                pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkQueryPool*                                pQueryPool) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    queryPool := ?
-    pQueryPool[0] = queryPool
-    State.QueryPools[queryPool] = new!QueryPoolObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyQueryPool(
-        VkDevice                                    device,
-        VkQueryPool                                 queryPool,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    queryPoolObject := GetQueryPool(queryPool)
-    assert(queryPoolObject.device == device)
-
-    State.QueryPools[queryPool] = null
-}
-
-@threadSafety("system")
-cmd VkResult vkGetQueryPoolResults(
-        VkDevice                                    device,
-        VkQueryPool                                 queryPool,
-        u32                                         firstQuery,
-        u32                                         queryCount,
-        platform.size_t                             dataSize,
-        void*                                       pData,
-        VkDeviceSize                                stride,
-        VkQueryResultFlags                          flags) {
-    deviceObject := GetDevice(device)
-    queryPoolObject := GetQueryPool(queryPool)
-    assert(queryPoolObject.device == device)
-
-    data := pData[0:dataSize]
-
-    return ?
-}
-
-// Buffer functions
-
-@threadSafety("system")
-cmd VkResult vkCreateBuffer(
-        VkDevice                                    device,
-        const VkBufferCreateInfo*                   pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkBuffer*                                   pBuffer) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    buffer := ?
-    pBuffer[0] = buffer
-    State.Buffers[buffer] = new!BufferObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyBuffer(
-        VkDevice                                    device,
-        VkBuffer                                    buffer,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    bufferObject := GetBuffer(buffer)
-    assert(bufferObject.device == device)
-
-    assert(bufferObject.memory == 0)
-    State.Buffers[buffer] = null
-}
-
-
-// Buffer view functions
-
-@threadSafety("system")
-cmd VkResult vkCreateBufferView(
-        VkDevice                                    device,
-        const VkBufferViewCreateInfo*               pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkBufferView*                               pView) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    bufferObject := GetBuffer(pCreateInfo.buffer)
-    assert(bufferObject.device == device)
-
-    view := ?
-    pView[0] = view
-    State.BufferViews[view] = new!BufferViewObject(device: device, buffer: pCreateInfo.buffer)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyBufferView(
-        VkDevice                                    device,
-        VkBufferView                                bufferView,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    bufferViewObject := GetBufferView(bufferView)
-    assert(bufferViewObject.device == device)
-
-    State.BufferViews[bufferView] = null
-}
-
-
-// Image functions
-
-@threadSafety("system")
-cmd VkResult vkCreateImage(
-        VkDevice                                    device,
-        const VkImageCreateInfo*                    pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkImage*                                    pImage) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    image := ?
-    pImage[0] = image
-    State.Images[image] = new!ImageObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyImage(
-        VkDevice                                    device,
-        VkImage                                     image,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    imageObject := GetImage(image)
-    assert(imageObject.device == device)
-
-    assert(imageObject.memory == 0)
-    State.Images[image] = null
-}
-
-cmd void vkGetImageSubresourceLayout(
-        VkDevice                                    device,
-        VkImage                                     image,
-        const VkImageSubresource*                   pSubresource,
-        VkSubresourceLayout*                        pLayout) {
-    deviceObject := GetDevice(device)
-    imageObject := GetImage(image)
-    assert(imageObject.device == device)
-}
-
-
-// Image view functions
-
-@threadSafety("system")
-cmd VkResult vkCreateImageView(
-        VkDevice                                    device,
-        const VkImageViewCreateInfo*                pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkImageView*                                pView) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    imageObject := GetImage(pCreateInfo.image)
-    assert(imageObject.device == device)
-
-    view := ?
-    pView[0] = view
-    State.ImageViews[view] = new!ImageViewObject(device: device, image: pCreateInfo.image)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyImageView(
-        VkDevice                                    device,
-        VkImageView                                 imageView,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    imageViewObject := GetImageView(imageView)
-    assert(imageViewObject.device == device)
-
-    State.ImageViews[imageView] = null
-}
-
-
-// Shader functions
-
-cmd VkResult vkCreateShaderModule(
-        VkDevice                                    device,
-        const VkShaderModuleCreateInfo*             pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkShaderModule*                             pShaderModule) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    shaderModule := ?
-    pShaderModule[0] = shaderModule
-    State.ShaderModules[shaderModule] = new!ShaderModuleObject(device: device)
-
-    return ?
-}
-
-cmd void vkDestroyShaderModule(
-        VkDevice                                    device,
-        VkShaderModule                              shaderModule,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    shaderModuleObject := GetShaderModule(shaderModule)
-    assert(shaderModuleObject.device == device)
-
-    State.ShaderModules[shaderModule] = null
-}
-
-
-// Pipeline functions
-
-cmd VkResult vkCreatePipelineCache(
-        VkDevice                                    device,
-        const VkPipelineCacheCreateInfo*            pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkPipelineCache*                            pPipelineCache) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    pipelineCache := ?
-    pPipelineCache[0] = pipelineCache
-    State.PipelineCaches[pipelineCache] = new!PipelineCacheObject(device: device)
-
-    return ?
-}
-
-cmd void vkDestroyPipelineCache(
-        VkDevice                                    device,
-        VkPipelineCache                             pipelineCache,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    pipelineCacheObject := GetPipelineCache(pipelineCache)
-    assert(pipelineCacheObject.device == device)
-
-    State.PipelineCaches[pipelineCache] = null
-}
-
-cmd VkResult vkGetPipelineCacheData(
-        VkDevice                                    device,
-        VkPipelineCache                             pipelineCache,
-        platform.size_t*                            pDataSize,
-        void*                                       pData) {
-    deviceObject := GetDevice(device)
-    pipelineCacheObject := GetPipelineCache(pipelineCache)
-    assert(pipelineCacheObject.device == device)
-
-    return ?
-}
-
-cmd VkResult vkMergePipelineCaches(
-        VkDevice                                    device,
-        VkPipelineCache                             dstCache,
-        u32                                         srcCacheCount,
-        const VkPipelineCache*                      pSrcCaches) {
-    deviceObject := GetDevice(device)
-    dstCacheObject := GetPipelineCache(dstCache)
-    assert(dstCacheObject.device == device)
-
-    srcCaches := pSrcCaches[0:srcCacheCount]
-    for i in (0 .. srcCacheCount) {
-        srcCache := srcCaches[i]
-        srcCacheObject := GetPipelineCache(srcCache)
-        assert(srcCacheObject.device == device)
-    }
-
-    return ?
-}
-
-cmd VkResult vkCreateGraphicsPipelines(
-        VkDevice                                    device,
-        VkPipelineCache                             pipelineCache,
-        u32                                         createInfoCount,
-        const VkGraphicsPipelineCreateInfo*         pCreateInfos,
-        const VkAllocationCallbacks*                pAllocator,
-        VkPipeline*                                 pPipelines) {
-    deviceObject := GetDevice(device)
-    if pipelineCache != NULL_HANDLE {
-        pipelineCacheObject := GetPipelineCache(pipelineCache)
-        assert(pipelineCacheObject.device == device)
-    }
-
-    createInfos := pCreateInfos[0:createInfoCount]
-    pipelines := pPipelines[0:createInfoCount]
-    for i in (0 .. createInfoCount) {
-        pipeline := ?
-        pipelines[i] = pipeline
-        State.Pipelines[pipeline] = new!PipelineObject(device: device)
-    }
-
-    return ?
-}
-
-cmd VkResult vkCreateComputePipelines(
-        VkDevice                                    device,
-        VkPipelineCache                             pipelineCache,
-        u32                                         createInfoCount,
-        const VkComputePipelineCreateInfo*          pCreateInfos,
-        const VkAllocationCallbacks*                pAllocator,
-        VkPipeline*                                 pPipelines) {
-    deviceObject := GetDevice(device)
-    if pipelineCache != NULL_HANDLE {
-        pipelineCacheObject := GetPipelineCache(pipelineCache)
-        assert(pipelineCacheObject.device == device)
-    }
-
-    createInfos := pCreateInfos[0:createInfoCount]
-    pipelines := pPipelines[0:createInfoCount]
-    for i in (0 .. createInfoCount) {
-        pipeline := ?
-        pipelines[i] = pipeline
-        State.Pipelines[pipeline] = new!PipelineObject(device: device)
-    }
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyPipeline(
-        VkDevice                                    device,
-        VkPipeline                                  pipeline,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    pipelineObjects := GetPipeline(pipeline)
-    assert(pipelineObjects.device == device)
-
-    State.Pipelines[pipeline] = null
-}
-
-
-// Pipeline layout functions
-
-@threadSafety("system")
-cmd VkResult vkCreatePipelineLayout(
-        VkDevice                                    device,
-        const VkPipelineLayoutCreateInfo*           pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkPipelineLayout*                           pPipelineLayout) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    pipelineLayout := ?
-    pPipelineLayout[0] = pipelineLayout
-    State.PipelineLayouts[pipelineLayout] = new!PipelineLayoutObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyPipelineLayout(
-        VkDevice                                    device,
-        VkPipelineLayout                            pipelineLayout,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    pipelineLayoutObjects := GetPipelineLayout(pipelineLayout)
-    assert(pipelineLayoutObjects.device == device)
-
-    State.PipelineLayouts[pipelineLayout] = null
-}
-
-
-// Sampler functions
-
-@threadSafety("system")
-cmd VkResult vkCreateSampler(
-        VkDevice                                    device,
-        const VkSamplerCreateInfo*                  pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkSampler*                                  pSampler) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    sampler := ?
-    pSampler[0] = sampler
-    State.Samplers[sampler] = new!SamplerObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroySampler(
-        VkDevice                                    device,
-        VkSampler                                   sampler,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    samplerObject := GetSampler(sampler)
-    assert(samplerObject.device == device)
-
-    State.Samplers[sampler] = null
-}
-
-
-// Descriptor set functions
-
-@threadSafety("system")
-cmd VkResult vkCreateDescriptorSetLayout(
-        VkDevice                                    device,
-        const VkDescriptorSetLayoutCreateInfo*      pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDescriptorSetLayout*                      pSetLayout) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    setLayout := ?
-    pSetLayout[0] = setLayout
-    State.DescriptorSetLayouts[setLayout] = new!DescriptorSetLayoutObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyDescriptorSetLayout(
-        VkDevice                                    device,
-        VkDescriptorSetLayout                       descriptorSetLayout,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    descriptorSetLayoutObject := GetDescriptorSetLayout(descriptorSetLayout)
-    assert(descriptorSetLayoutObject.device == device)
-
-    State.DescriptorSetLayouts[descriptorSetLayout] = null
-}
-
-@threadSafety("system")
-cmd VkResult vkCreateDescriptorPool(
-        VkDevice                                    device,
-        const VkDescriptorPoolCreateInfo*           pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDescriptorPool*                           pDescriptorPool) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    descriptorPool := ?
-    pDescriptorPool[0] = descriptorPool
-    State.DescriptorPools[descriptorPool] = new!DescriptorPoolObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyDescriptorPool(
-        VkDevice                                    device,
-        VkDescriptorPool                            descriptorPool,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    descriptorPoolObject := GetDescriptorPool(descriptorPool)
-    assert(descriptorPoolObject.device == device)
-
-    State.DescriptorPools[descriptorPool] = null
-}
-
-@threadSafety("app")
-cmd VkResult vkResetDescriptorPool(
-        VkDevice                                    device,
-        VkDescriptorPool                            descriptorPool,
-        VkDescriptorPoolResetFlags                  flags) {
-    deviceObject := GetDevice(device)
-    descriptorPoolObject := GetDescriptorPool(descriptorPool)
-    assert(descriptorPoolObject.device == device)
-
-    return ?
-}
-
-@threadSafety("app")
-cmd VkResult vkAllocateDescriptorSets(
-        VkDevice                                    device,
-        const VkDescriptorSetAllocateInfo*          pAllocateInfo,
-        VkDescriptorSet*                            pDescriptorSets) {
-    deviceObject := GetDevice(device)
-    allocInfo := pAllocateInfo[0]
-    descriptorPoolObject := GetDescriptorPool(allocInfo.descriptorPool)
-
-    setLayouts := allocInfo.pSetLayouts[0:allocInfo.setCount]
-    for i in (0 .. allocInfo.setCount) {
-        setLayout := setLayouts[i]
-        setLayoutObject := GetDescriptorSetLayout(setLayout)
-        assert(setLayoutObject.device == device)
-    }
-
-    descriptorSets := pDescriptorSets[0:allocInfo.setCount]
-    for i in (0 .. allocInfo.setCount) {
-        descriptorSet := ?
-        descriptorSets[i] = descriptorSet
-        State.DescriptorSets[descriptorSet] = new!DescriptorSetObject(device: device)
-    }
-
-    return ?
-}
-
-cmd VkResult vkFreeDescriptorSets(
-        VkDevice                                    device,
-        VkDescriptorPool                            descriptorPool,
-        u32                                         descriptorSetCount,
-        const VkDescriptorSet*                      pDescriptorSets) {
-    deviceObject := GetDevice(device)
-    descriptorPoolObject := GetDescriptorPool(descriptorPool)
-
-    descriptorSets := pDescriptorSets[0:descriptorSetCount]
-    for i in (0 .. descriptorSetCount) {
-        descriptorSet := descriptorSets[i]
-        descriptorSetObject := GetDescriptorSet(descriptorSet)
-        assert(descriptorSetObject.device == device)
-        State.DescriptorSets[descriptorSet] = null
-    }
-
-    return ?
-}
-
-cmd void vkUpdateDescriptorSets(
-        VkDevice                                    device,
-        u32                                         descriptorWriteCount,
-        const VkWriteDescriptorSet*                 pDescriptorWrites,
-        u32                                         descriptorCopyCount,
-        const VkCopyDescriptorSet*                  pDescriptorCopies) {
-    deviceObject := GetDevice(device)
-
-    descriptorWrites := pDescriptorWrites[0:descriptorWriteCount]
-    for i in (0 .. descriptorWriteCount) {
-        descriptorWrite := descriptorWrites[i]
-        descriptorWriteObject := GetDescriptorSet(descriptorWrite.dstSet)
-        assert(descriptorWriteObject.device == device)
-    }
-
-    descriptorCopies := pDescriptorCopies[0:descriptorCopyCount]
-    for i in (0 .. descriptorCopyCount) {
-        descriptorCopy := descriptorCopies[i]
-        descriptorCopyObject := GetDescriptorSet(descriptorCopy.dstSet)
-        assert(descriptorCopyObject.device == device)
-    }
-}
-
-
-// Framebuffer functions
-
-@threadSafety("system")
-cmd VkResult vkCreateFramebuffer(
-        VkDevice                                    device,
-        const VkFramebufferCreateInfo*              pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkFramebuffer*                              pFramebuffer) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    framebuffer := ?
-    pFramebuffer[0] = framebuffer
-    State.Framebuffers[framebuffer] = new!FramebufferObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyFramebuffer(
-        VkDevice                                    device,
-        VkFramebuffer                               framebuffer,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    framebufferObject := GetFramebuffer(framebuffer)
-    assert(framebufferObject.device == device)
-
-    State.Framebuffers[framebuffer] = null
-}
-
-
-// Renderpass functions
-
-@threadSafety("system")
-cmd VkResult vkCreateRenderPass(
-        VkDevice                                    device,
-        const VkRenderPassCreateInfo*               pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkRenderPass*                               pRenderPass) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    renderpass := ?
-    pRenderPass[0] = renderpass
-    State.RenderPasses[renderpass] = new!RenderPassObject(device: device)
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkDestroyRenderPass(
-        VkDevice                                    device,
-        VkRenderPass                                renderPass,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    renderPassObject := GetRenderPass(renderPass)
-    assert(renderPassObject.device == device)
-
-    State.RenderPasses[renderPass] = null
-}
-
-cmd void vkGetRenderAreaGranularity(
-        VkDevice                                    device,
-        VkRenderPass                                renderPass,
-        VkExtent2D*                                 pGranularity) {
-    deviceObject := GetDevice(device)
-    renderPassObject := GetRenderPass(renderPass)
-
-    granularity := ?
-    pGranularity[0] = granularity
-}
-
-// Command pool functions
-
-cmd VkResult vkCreateCommandPool(
-        VkDevice                                    device,
-        const VkCommandPoolCreateInfo*              pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkCommandPool*                              pCommandPool) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO)
-    deviceObject := GetDevice(device)
-
-    commandPool := ?
-    pCommandPool[0] = commandPool
-    State.CommandPools[commandPool] = new!CommandPoolObject(device: device)
-
-    return ?
-}
-
-cmd void vkDestroyCommandPool(
-        VkDevice                                    device,
-        VkCommandPool                               commandPool,
-        const VkAllocationCallbacks*                pAllocator) {
-    deviceObject := GetDevice(device)
-    commandPoolObject := GetCommandPool(commandPool)
-    assert(commandPoolObject.device == device)
-
-    State.CommandPools[commandPool] = null
-}
-
-cmd VkResult vkResetCommandPool(
-        VkDevice                                    device,
-        VkCommandPool                               commandPool,
-        VkCommandPoolResetFlags                     flags) {
-    deviceObject := GetDevice(device)
-    commandPoolObject := GetCommandPool(commandPool)
-    assert(commandPoolObject.device == device)
-
-    return ?
-}
-
-// Command buffer functions
-
-macro void bindCommandBuffer(VkCommandBuffer commandBuffer, any obj, VkDeviceMemory memory) {
-    memoryObject := GetDeviceMemory(memory)
-    memoryObject.boundCommandBuffers[commandBuffer] = commandBuffer
-
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.boundObjects[as!u64(obj)] = memory
-}
-
-macro void unbindCommandBuffer(VkCommandBuffer commandBuffer, any obj, VkDeviceMemory memory) {
-    memoryObject := GetDeviceMemory(memory)
-    memoryObject.boundCommandBuffers[commandBuffer] = null
-
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.boundObjects[as!u64(obj)] = null
-}
-
-@threadSafety("system")
-cmd VkResult vkAllocateCommandBuffers(
-        VkDevice                                    device,
-        const VkCommandBufferAllocateInfo*          pAllocateInfo,
-        VkCommandBuffer*                            pCommandBuffers) {
-    assert(pAllocateInfo[0].sType == VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO)
-
-    count := pAllocateInfo[0].commandBufferCount
-    commandBuffers := pCommandBuffers[0:count]
-    for i in (0 .. count) {
-        commandBuffer := ?
-        commandBuffers[i] = commandBuffer
-        State.CommandBuffers[commandBuffer] = new!CommandBufferObject(device: device)
-    }
-
-    return ?
-}
-
-@threadSafety("system")
-cmd void vkFreeCommandBuffers(
-        VkDevice                                    device,
-        VkCommandPool                               commandPool,
-        u32                                         commandBufferCount,
-        const VkCommandBuffer*                      pCommandBuffers) {
-    deviceObject := GetDevice(device)
-
-    commandBuffers := pCommandBuffers[0:commandBufferCount]
-    for i in (0 .. commandBufferCount) {
-        commandBufferObject := GetCommandBuffer(commandBuffers[i])
-        assert(commandBufferObject.device == device)
-        // TODO: iterate over boundObjects and clear memory bindings
-        State.CommandBuffers[commandBuffers[i]] = null
-    }
-}
-
-@threadSafety("app")
-cmd VkResult vkBeginCommandBuffer(
-        VkCommandBuffer                             commandBuffer,
-        const VkCommandBufferBeginInfo*             pBeginInfo) {
-    assert(pBeginInfo.sType == VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO)
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    // TODO: iterate over boundObjects and clear memory bindings
-
-    return ?
-}
-
-@threadSafety("app")
-cmd VkResult vkEndCommandBuffer(
-        VkCommandBuffer                             commandBuffer) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    return ?
-}
-
-@threadSafety("app")
-cmd VkResult vkResetCommandBuffer(
-        VkCommandBuffer                             commandBuffer,
-        VkCommandBufferResetFlags                   flags) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    // TODO: iterate over boundObjects and clear memory bindings
-
-    return ?
-}
-
-
-// Command buffer building functions
-
-@threadSafety("app")
-cmd void vkCmdBindPipeline(
-        VkCommandBuffer                             commandBuffer,
-        VkPipelineBindPoint                         pipelineBindPoint,
-        VkPipeline                                  pipeline) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    pipelineObject := GetPipeline(pipeline)
-    assert(commandBufferObject.device == pipelineObject.device)
-
-    queue := switch (pipelineBindPoint) {
-        case VK_PIPELINE_BIND_POINT_COMPUTE:  VK_QUEUE_COMPUTE_BIT
-        case VK_PIPELINE_BIND_POINT_GRAPHICS: VK_QUEUE_GRAPHICS_BIT
-    }
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, queue)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetViewport(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstViewport,
-        u32                                         viewportCount,
-        const VkViewport*                           pViewports) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetScissor(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstScissor,
-        u32                                         scissorCount,
-        const VkRect2D*                             pScissors) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetLineWidth(
-        VkCommandBuffer                             commandBuffer,
-        f32                                         lineWidth) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetDepthBias(
-        VkCommandBuffer                             commandBuffer,
-        f32                                         depthBiasConstantFactor,
-        f32                                         depthBiasClamp,
-        f32                                         depthBiasSlopeFactor) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetBlendConstants(
-        VkCommandBuffer                             commandBuffer,
-        // TODO(jessehall): apic only supports 'const' on pointer types. Using
-        // an annotation as a quick hack to pass this to the template without
-        // having to modify the AST and semantic model.
-        @readonly f32[4]                            blendConstants) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetDepthBounds(
-        VkCommandBuffer                             commandBuffer,
-        f32                                         minDepthBounds,
-        f32                                         maxDepthBounds) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetStencilCompareMask(
-        VkCommandBuffer                             commandBuffer,
-        VkStencilFaceFlags                          faceMask,
-        u32                                         compareMask) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetStencilWriteMask(
-        VkCommandBuffer                             commandBuffer,
-        VkStencilFaceFlags                          faceMask,
-        u32                                         writeMask) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetStencilReference(
-        VkCommandBuffer                             commandBuffer,
-        VkStencilFaceFlags                          faceMask,
-        u32                                         reference) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdBindDescriptorSets(
-        VkCommandBuffer                             commandBuffer,
-        VkPipelineBindPoint                         pipelineBindPoint,
-        VkPipelineLayout                            layout,
-        u32                                         firstSet,
-        u32                                         descriptorSetCount,
-        const VkDescriptorSet*                      pDescriptorSets,
-        u32                                         dynamicOffsetCount,
-        const u32*                                  pDynamicOffsets) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    descriptorSets := pDescriptorSets[0:descriptorSetCount]
-    for i in (0 .. descriptorSetCount) {
-        descriptorSet := descriptorSets[i]
-        descriptorSetObject := GetDescriptorSet(descriptorSet)
-        assert(commandBufferObject.device == descriptorSetObject.device)
-    }
-
-    dynamicOffsets := pDynamicOffsets[0:dynamicOffsetCount]
-    for i in (0 .. dynamicOffsetCount) {
-        dynamicOffset := dynamicOffsets[i]
-    }
-
-    queue := switch (pipelineBindPoint) {
-        case VK_PIPELINE_BIND_POINT_COMPUTE:  VK_QUEUE_COMPUTE_BIT
-        case VK_PIPELINE_BIND_POINT_GRAPHICS: VK_QUEUE_GRAPHICS_BIT
-    }
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, queue)
-}
-
-@threadSafety("app")
-cmd void vkCmdBindIndexBuffer(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        VkIndexType                                 indexType) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    bufferObject := GetBuffer(buffer)
-    assert(commandBufferObject.device == bufferObject.device)
-
-    bindCommandBuffer(commandBuffer, buffer, bufferObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdBindVertexBuffers(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstBinding,
-        u32                                         bindingCount,
-        const VkBuffer*                             pBuffers,
-        const VkDeviceSize*                         pOffsets) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    // TODO: check if not [firstBinding:firstBinding+bindingCount]
-    buffers := pBuffers[0:bindingCount]
-    offsets := pOffsets[0:bindingCount]
-    for i in (0 .. bindingCount) {
-        buffer := buffers[i]
-        offset := offsets[i]
-        bufferObject := GetBuffer(buffer)
-        assert(commandBufferObject.device == bufferObject.device)
-
-        bindCommandBuffer(commandBuffer, buffer, bufferObject.memory)
-    }
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdDraw(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         vertexCount,
-        u32                                         instanceCount,
-        u32                                         firstVertex,
-        u32                                         firstInstance) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdDrawIndexed(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         indexCount,
-        u32                                         instanceCount,
-        u32                                         firstIndex,
-        s32                                         vertexOffset,
-        u32                                         firstInstance) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdDrawIndirect(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        u32                                         drawCount,
-        u32                                         stride) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    bufferObject := GetBuffer(buffer)
-    assert(commandBufferObject.device == bufferObject.device)
-
-    bindCommandBuffer(commandBuffer, buffer, bufferObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdDrawIndexedIndirect(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        u32                                         drawCount,
-        u32                                         stride) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    bufferObject := GetBuffer(buffer)
-    assert(commandBufferObject.device == bufferObject.device)
-
-    bindCommandBuffer(commandBuffer, buffer, bufferObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdDispatch(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         groupCountX,
-        u32                                         groupCountY,
-        u32                                         groupCountZ) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_COMPUTE_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdDispatchIndirect(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    bufferObject := GetBuffer(buffer)
-    assert(commandBufferObject.device == bufferObject.device)
-
-    bindCommandBuffer(commandBuffer, buffer, bufferObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_COMPUTE_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdCopyBuffer(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    srcBuffer,
-        VkBuffer                                    dstBuffer,
-        u32                                         regionCount,
-        const VkBufferCopy*                         pRegions) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    srcBufferObject := GetBuffer(srcBuffer)
-    dstBufferObject := GetBuffer(dstBuffer)
-    assert(commandBufferObject.device == srcBufferObject.device)
-    assert(commandBufferObject.device == dstBufferObject.device)
-
-    regions := pRegions[0:regionCount]
-    for i in (0 .. regionCount) {
-        region := regions[i]
-    }
-
-    bindCommandBuffer(commandBuffer, srcBuffer, srcBufferObject.memory)
-    bindCommandBuffer(commandBuffer, dstBuffer, dstBufferObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdCopyImage(
-        VkCommandBuffer                             commandBuffer,
-        VkImage                                     srcImage,
-        VkImageLayout                               srcImageLayout,
-        VkImage                                     dstImage,
-        VkImageLayout                               dstImageLayout,
-        u32                                         regionCount,
-        const VkImageCopy*                          pRegions) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    srcImageObject := GetImage(srcImage)
-    dstImageObject := GetImage(dstImage)
-    assert(commandBufferObject.device == srcImageObject.device)
-    assert(commandBufferObject.device == dstImageObject.device)
-
-    regions := pRegions[0:regionCount]
-    for i in (0 .. regionCount) {
-        region := regions[i]
-    }
-
-    bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory)
-    bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdBlitImage(
-        VkCommandBuffer                             commandBuffer,
-        VkImage                                     srcImage,
-        VkImageLayout                               srcImageLayout,
-        VkImage                                     dstImage,
-        VkImageLayout                               dstImageLayout,
-        u32                                         regionCount,
-        const VkImageBlit*                          pRegions,
-        VkFilter                                    filter) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    srcImageObject := GetImage(srcImage)
-    dstImageObject := GetImage(dstImage)
-    assert(commandBufferObject.device == srcImageObject.device)
-    assert(commandBufferObject.device == dstImageObject.device)
-
-    regions := pRegions[0:regionCount]
-    for i in (0 .. regionCount) {
-        region := regions[i]
-    }
-
-    bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory)
-    bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdCopyBufferToImage(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    srcBuffer,
-        VkImage                                     dstImage,
-        VkImageLayout                               dstImageLayout,
-        u32                                         regionCount,
-        const VkBufferImageCopy*                    pRegions) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    srcBufferObject := GetBuffer(srcBuffer)
-    dstImageObject := GetImage(dstImage)
-    assert(commandBufferObject.device == srcBufferObject.device)
-    assert(commandBufferObject.device == dstImageObject.device)
-
-    regions := pRegions[0:regionCount]
-    for i in (0 .. regionCount) {
-        region := regions[i]
-    }
-
-    bindCommandBuffer(commandBuffer, srcBuffer, srcBufferObject.memory)
-    bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdCopyImageToBuffer(
-        VkCommandBuffer                             commandBuffer,
-        VkImage                                     srcImage,
-        VkImageLayout                               srcImageLayout,
-        VkBuffer                                    dstBuffer,
-        u32                                         regionCount,
-        const VkBufferImageCopy*                    pRegions) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    srcImageObject := GetImage(srcImage)
-    dstBufferObject := GetBuffer(dstBuffer)
-    assert(commandBufferObject.device == srcImageObject.device)
-    assert(commandBufferObject.device == dstBufferObject.device)
-
-    regions := pRegions[0:regionCount]
-    for i in (0 .. regionCount) {
-        region := regions[i]
-    }
-
-    bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory)
-    bindCommandBuffer(commandBuffer, dstBuffer, dstBufferObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdUpdateBuffer(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    dstBuffer,
-        VkDeviceSize                                dstOffset,
-        VkDeviceSize                                dataSize,
-        const void*                                 pData) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    dstBufferObject := GetBuffer(dstBuffer)
-    assert(commandBufferObject.device == dstBufferObject.device)
-
-    data := pData[0:dataSize]
-
-    bindCommandBuffer(commandBuffer, dstBuffer, dstBufferObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdFillBuffer(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    dstBuffer,
-        VkDeviceSize                                dstOffset,
-        VkDeviceSize                                size,
-        u32                                         data) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    dstBufferObject := GetBuffer(dstBuffer)
-    assert(commandBufferObject.device == dstBufferObject.device)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdClearColorImage(
-        VkCommandBuffer                             commandBuffer,
-        VkImage                                     image,
-        VkImageLayout                               imageLayout,
-        const VkClearColorValue*                    pColor,
-        u32                                         rangeCount,
-        const VkImageSubresourceRange*              pRanges) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    imageObject := GetImage(image)
-    assert(commandBufferObject.device == imageObject.device)
-
-    ranges := pRanges[0:rangeCount]
-    for i in (0 .. rangeCount) {
-        range := ranges[i]
-    }
-
-    bindCommandBuffer(commandBuffer, image, imageObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdClearDepthStencilImage(
-        VkCommandBuffer                             commandBuffer,
-        VkImage                                     image,
-        VkImageLayout                               imageLayout,
-        const VkClearDepthStencilValue*             pDepthStencil,
-        u32                                         rangeCount,
-        const VkImageSubresourceRange*              pRanges) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    imageObject := GetImage(image)
-    assert(commandBufferObject.device == imageObject.device)
-
-    ranges := pRanges[0:rangeCount]
-    for i in (0 .. rangeCount) {
-        range := ranges[i]
-    }
-
-    bindCommandBuffer(commandBuffer, image, imageObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdClearAttachments(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         attachmentCount,
-        const VkClearAttachment*                    pAttachments,
-        u32                                         rectCount,
-        const VkClearRect*                          pRects) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    rects := pRects[0:rectCount]
-    for i in (0 .. rectCount) {
-        rect := rects[i]
-    }
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdResolveImage(
-        VkCommandBuffer                             commandBuffer,
-        VkImage                                     srcImage,
-        VkImageLayout                               srcImageLayout,
-        VkImage                                     dstImage,
-        VkImageLayout                               dstImageLayout,
-        u32                                         regionCount,
-        const VkImageResolve*                       pRegions) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    srcImageObject := GetImage(srcImage)
-    dstImageObject := GetImage(dstImage)
-    assert(commandBufferObject.device == srcImageObject.device)
-    assert(commandBufferObject.device == dstImageObject.device)
-
-    regions := pRegions[0:regionCount]
-    for i in (0 .. regionCount) {
-        region := regions[i]
-    }
-
-    bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory)
-    bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-@threadSafety("app")
-cmd void vkCmdSetEvent(
-        VkCommandBuffer                             commandBuffer,
-        VkEvent                                     event,
-        VkPipelineStageFlags                        stageMask) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    eventObject := GetEvent(event)
-    assert(commandBufferObject.device == eventObject.device)
-}
-
-@threadSafety("app")
-cmd void vkCmdResetEvent(
-        VkCommandBuffer                             commandBuffer,
-        VkEvent                                     event,
-        VkPipelineStageFlags                        stageMask) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    eventObject := GetEvent(event)
-    assert(commandBufferObject.device == eventObject.device)
-}
-
-@threadSafety("app")
-cmd void vkCmdWaitEvents(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         eventCount,
-        const VkEvent*                              pEvents,
-        VkPipelineStageFlags                        srcStageMask,
-        VkPipelineStageFlags                        dstStageMask,
-        u32                                         memoryBarrierCount,
-        const VkMemoryBarrier*                      pMemoryBarriers,
-        u32                                         bufferMemoryBarrierCount,
-        const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
-        u32                                         imageMemoryBarrierCount,
-        const VkImageMemoryBarrier*                 pImageMemoryBarriers) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    events := pEvents[0:eventCount]
-    for i in (0 .. eventCount) {
-        event := events[i]
-        eventObject := GetEvent(event)
-        assert(commandBufferObject.device == eventObject.device)
-    }
-
-    memoryBarriers := pMemoryBarriers[0:memoryBarrierCount]
-    for i in (0 .. memoryBarrierCount) {
-        memoryBarrier := memoryBarriers[i]
-    }
-    bufferMemoryBarriers := pBufferMemoryBarriers[0:bufferMemoryBarrierCount]
-    for i in (0 .. bufferMemoryBarrierCount) {
-        bufferMemoryBarrier := bufferMemoryBarriers[i]
-        bufferObject := GetBuffer(bufferMemoryBarrier.buffer)
-        assert(bufferObject.device == commandBufferObject.device)
-    }
-    imageMemoryBarriers := pImageMemoryBarriers[0:imageMemoryBarrierCount]
-    for i in (0 .. imageMemoryBarrierCount) {
-        imageMemoryBarrier := imageMemoryBarriers[i]
-        imageObject := GetImage(imageMemoryBarrier.image)
-        assert(imageObject.device == commandBufferObject.device)
-    }
-}
-
-@threadSafety("app")
-cmd void vkCmdPipelineBarrier(
-        VkCommandBuffer                             commandBuffer,
-        VkPipelineStageFlags                        srcStageMask,
-        VkPipelineStageFlags                        dstStageMask,
-        VkDependencyFlags                           dependencyFlags,
-        u32                                         memoryBarrierCount,
-        const VkMemoryBarrier*                      pMemoryBarriers,
-        u32                                         bufferMemoryBarrierCount,
-        const VkBufferMemoryBarrier*                pBufferMemoryBarriers,
-        u32                                         imageMemoryBarrierCount,
-        const VkImageMemoryBarrier*                 pImageMemoryBarriers) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    memoryBarriers := pMemoryBarriers[0:memoryBarrierCount]
-    for i in (0 .. memoryBarrierCount) {
-        memoryBarrier := memoryBarriers[i]
-    }
-    bufferMemoryBarriers := pBufferMemoryBarriers[0:bufferMemoryBarrierCount]
-    for i in (0 .. bufferMemoryBarrierCount) {
-        bufferMemoryBarrier := bufferMemoryBarriers[i]
-        bufferObject := GetBuffer(bufferMemoryBarrier.buffer)
-        assert(bufferObject.device == commandBufferObject.device)
-    }
-    imageMemoryBarriers := pImageMemoryBarriers[0:imageMemoryBarrierCount]
-    for i in (0 .. imageMemoryBarrierCount) {
-        imageMemoryBarrier := imageMemoryBarriers[i]
-        imageObject := GetImage(imageMemoryBarrier.image)
-        assert(imageObject.device == commandBufferObject.device)
-    }
-}
-
-@threadSafety("app")
-cmd void vkCmdBeginQuery(
-        VkCommandBuffer                             commandBuffer,
-        VkQueryPool                                 queryPool,
-        u32                                         query,
-        VkQueryControlFlags                         flags) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    queryPoolObject := GetQueryPool(queryPool)
-    assert(commandBufferObject.device == queryPoolObject.device)
-}
-
-@threadSafety("app")
-cmd void vkCmdEndQuery(
-        VkCommandBuffer                             commandBuffer,
-        VkQueryPool                                 queryPool,
-        u32                                         query) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    queryPoolObject := GetQueryPool(queryPool)
-    assert(commandBufferObject.device == queryPoolObject.device)
-}
-
-@threadSafety("app")
-cmd void vkCmdResetQueryPool(
-        VkCommandBuffer                             commandBuffer,
-        VkQueryPool                                 queryPool,
-        u32                                         firstQuery,
-        u32                                         queryCount) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    queryPoolObject := GetQueryPool(queryPool)
-    assert(commandBufferObject.device == queryPoolObject.device)
-}
-
-@threadSafety("app")
-cmd void vkCmdWriteTimestamp(
-        VkCommandBuffer                             commandBuffer,
-        VkPipelineStageFlagBits                     pipelineStage,
-        VkQueryPool                                 queryPool,
-        u32                                         query) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    queryPoolObject := GetQueryPool(queryPool)
-    assert(commandBufferObject.device == queryPoolObject.device)
-}
-
-@threadSafety("app")
-cmd void vkCmdCopyQueryPoolResults(
-        VkCommandBuffer                             commandBuffer,
-        VkQueryPool                                 queryPool,
-        u32                                         firstQuery,
-        u32                                         queryCount,
-        VkBuffer                                    dstBuffer,
-        VkDeviceSize                                dstOffset,
-        VkDeviceSize                                stride,
-        VkQueryResultFlags                          flags) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    queryPoolObject := GetQueryPool(queryPool)
-    dstBufferObject := GetBuffer(dstBuffer)
-    assert(commandBufferObject.device == queryPoolObject.device)
-    assert(commandBufferObject.device == dstBufferObject.device)
-}
-
-cmd void vkCmdPushConstants(
-        VkCommandBuffer                             commandBuffer,
-        VkPipelineLayout                            layout,
-        VkShaderStageFlags                          stageFlags,
-        u32                                         offset,
-        u32                                         size,
-        const void*                                 pValues) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    layoutObject := GetPipelineLayout(layout)
-    assert(commandBufferObject.device == layoutObject.device)
-}
-
-@threadSafety("app")
-cmd void vkCmdBeginRenderPass(
-        VkCommandBuffer                             commandBuffer,
-        const VkRenderPassBeginInfo*                pRenderPassBegin,
-        VkSubpassContents                           contents) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-    renderPassObject := GetRenderPass(pRenderPassBegin.renderPass)
-    framebufferObject := GetFramebuffer(pRenderPassBegin.framebuffer)
-    assert(commandBufferObject.device == renderPassObject.device)
-    assert(commandBufferObject.device == framebufferObject.device)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-cmd void vkCmdNextSubpass(
-        VkCommandBuffer                             commandBuffer,
-        VkSubpassContents                           contents) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-}
-
-@threadSafety("app")
-cmd void vkCmdEndRenderPass(
-        VkCommandBuffer                             commandBuffer) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT)
-}
-
-cmd void vkCmdExecuteCommands(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         commandBufferCount,
-        const VkCommandBuffer*                      pCommandBuffers) {
-    commandBufferObject := GetCommandBuffer(commandBuffer)
-
-    commandBuffers := pCommandBuffers[0:commandBufferCount]
-    for i in (0 .. commandBufferCount) {
-        secondaryCommandBuffer := commandBuffers[i]
-        secondaryCommandBufferObject := GetCommandBuffer(secondaryCommandBuffer)
-        assert(commandBufferObject.device == secondaryCommandBufferObject.device)
-    }
-}
-
-//@vulkan1_1 functions
-
-@vulkan1_1
-cmd VkResult vkEnumerateInstanceVersion(
-        u32*                                            pApiVersion) {
-    return ?
-}
-
-@vulkan1_1
-cmd VkResult vkBindBufferMemory2(
-        VkDevice                                        device,
-        u32                                             bindInfoCount,
-        const VkBindBufferMemoryInfo*                   pBindInfos) {
-    return ?
-}
-
-@vulkan1_1
-cmd VkResult vkBindImageMemory2(
-        VkDevice                                        device,
-        u32                                             bindInfoCount,
-        const VkBindImageMemoryInfo*                    pBindInfos) {
-    return ?
-}
-
-@vulkan1_1
-cmd void vkGetDeviceGroupPeerMemoryFeatures(
-        VkDevice                                    device,
-        u32                                         heapIndex,
-        u32                                         localDeviceIndex,
-        u32                                         remoteDeviceIndex,
-        VkPeerMemoryFeatureFlags*                   pPeerMemoryFeatures) {
-}
-
-@vulkan1_1
-cmd void vkCmdSetDeviceMask(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         deviceMask) {
-}
-
-@vulkan1_1
-cmd void vkCmdDispatchBase(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         baseGroupX,
-        u32                                         baseGroupY,
-        u32                                         baseGroupZ,
-        u32                                         groupCountX,
-        u32                                         groupCountY,
-        u32                                         groupCountZ) {
-}
-
-@threadSafety("system")
-@vulkan1_1
-cmd VkResult vkEnumeratePhysicalDeviceGroups(
-        VkInstance                                  instance,
-        u32*                                        pPhysicalDeviceGroupCount,
-        VkPhysicalDeviceGroupProperties*            pPhysicalDeviceGroupProperties) {
-    instanceObject := GetInstance(instance)
-
-    physicalDeviceGroupCount := as!u32(?)
-    pPhysicalDeviceGroupCount[0] = physicalDeviceGroupCount
-    physicalDevices := pPhysicalDeviceGroupProperties[0:physicalDeviceGroupCount]
-
-    for i in (0 .. physicalDeviceGroupCount) {
-        physicalDevice := ?
-        physicalDevices[i] = physicalDevice
-        if !(physicalDevice in State.PhysicalDevices) {
-            State.PhysicalDevices[physicalDevice] = new!PhysicalDeviceObject(instance: instance)
-        }
-    }
-
-    return ?
-}
-
-@vulkan1_1
-cmd void vkGetImageMemoryRequirements2(
-        VkDevice                                    device,
-        const VkImageMemoryRequirementsInfo2*       pInfo,
-        VkMemoryRequirements2*                      pMemoryRequirements) {
-}
-
-@vulkan1_1
-cmd void vkGetBufferMemoryRequirements2(
-        VkDevice                                    device,
-        const VkBufferMemoryRequirementsInfo2*      pInfo,
-        VkMemoryRequirements2*                      pMemoryRequirements) {
-}
-
-@vulkan1_1
-cmd void vkGetImageSparseMemoryRequirements2(
-        VkDevice                                        device,
-        const VkImageSparseMemoryRequirementsInfo2*     pInfo,
-        u32*                                            pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements2*               pSparseMemoryRequirements) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceFeatures2(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceFeatures2*                  pFeatures) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceProperties2(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceProperties2*                pProperties) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceFormatProperties2(
-        VkPhysicalDevice                            physicalDevice,
-        VkFormat                                    format,
-        VkFormatProperties2*                        pFormatProperties) {
-}
-
-@vulkan1_1
-cmd VkResult vkGetPhysicalDeviceImageFormatProperties2(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceImageFormatInfo2*     pImageFormatInfo,
-        VkImageFormatProperties2*                   pImageFormatProperties) {
-    return ?
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceQueueFamilyProperties2(
-        VkPhysicalDevice                            physicalDevice,
-        u32*                                        pQueueFamilyPropertyCount,
-        VkQueueFamilyProperties2*                   pQueueFamilyProperties) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceMemoryProperties2(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceMemoryProperties2*          pMemoryProperties) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceSparseImageFormatProperties2(
-        VkPhysicalDevice                                    physicalDevice,
-        const VkPhysicalDeviceSparseImageFormatInfo2*       pFormatInfo,
-        u32*                                                pPropertyCount,
-        VkSparseImageFormatProperties2*                     pProperties) {
-}
-
-@vulkan1_1
-cmd void vkTrimCommandPool(
-        VkDevice                                    device,
-        VkCommandPool                               commandPool,
-        VkCommandPoolTrimFlags                      flags) {
-}
-
-
-@vulkan1_1
-cmd void vkGetDeviceQueue2(
-        VkDevice                                    device,
-        const VkDeviceQueueInfo2*                   pQueueInfo,
-        VkQueue*                                    pQueue) {
-    deviceObject := GetDevice(device)
-
-    queue := ?
-    pQueue[0] = queue
-
-    if !(queue in State.Queues) {
-        State.Queues[queue] = new!QueueObject(device: device)
-    }
-}
-
-@vulkan1_1
-cmd VkResult vkCreateSamplerYcbcrConversion(
-        VkDevice                                        device,
-        const VkSamplerYcbcrConversionCreateInfo*       pCreateInfo,
-        const VkAllocationCallbacks*                    pAllocator,
-        VkSamplerYcbcrConversion*                       pYcbcrConversion) {
-    return ?
-}
-
-@vulkan1_1
-cmd void vkDestroySamplerYcbcrConversion(
-        VkDevice                                        device,
-        VkSamplerYcbcrConversion                        ycbcrConversion,
-        const VkAllocationCallbacks*                    pAllocator) {
-}
-
-@vulkan1_1
-cmd VkResult vkCreateDescriptorUpdateTemplate(
-        VkDevice                                    device,
-        const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDescriptorUpdateTemplate*                 pDescriptorUpdateTemplate) {
-    return ?
-}
-
-@vulkan1_1
-cmd void vkDestroyDescriptorUpdateTemplate(
-        VkDevice                                    device,
-        VkDescriptorUpdateTemplate                  descriptorUpdateTemplate,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@vulkan1_1
-cmd void vkUpdateDescriptorSetWithTemplate(
-        VkDevice                                    device,
-        VkDescriptorSet                             descriptorSet,
-        VkDescriptorUpdateTemplate                  descriptorUpdateTemplate,
-        const void*                                 pData) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceExternalBufferProperties(
-        VkPhysicalDevice                                physicalDevice,
-        const VkPhysicalDeviceExternalBufferInfo*       pExternalBufferInfo,
-        VkExternalBufferProperties*                     pExternalBufferProperties) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceExternalFenceProperties(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceExternalFenceInfo*    pExternalFenceInfo,
-        VkExternalFenceProperties*                  pExternalFenceProperties) {
-}
-
-@vulkan1_1
-cmd void vkGetPhysicalDeviceExternalSemaphoreProperties(
-        VkPhysicalDevice                                physicalDevice,
-        const VkPhysicalDeviceExternalSemaphoreInfo*    pExternalSemaphoreInfo,
-        VkExternalSemaphoreProperties*                  pExternalSemaphoreProperties) {
-}
-
-@vulkan1_1
-cmd void vkGetDescriptorSetLayoutSupport(
-        VkDevice                                    device,
-        const VkDescriptorSetLayoutCreateInfo*      pCreateInfo,
-        VkDescriptorSetLayoutSupport*               pSupport) {
-}
-
-
-@extension("VK_KHR_surface") // 1
-cmd void vkDestroySurfaceKHR(
-        VkInstance                                  instance,
-        VkSurfaceKHR                                surface,
-        const VkAllocationCallbacks*                pAllocator) {
-    instanceObject := GetInstance(instance)
-    surfaceObject := GetSurface(surface)
-    assert(surfaceObject.instance == instance)
-
-    State.Surfaces[surface] = null
-}
-
-@extension("VK_KHR_surface") // 1
-cmd VkResult vkGetPhysicalDeviceSurfaceSupportKHR(
-        VkPhysicalDevice                            physicalDevice,
-        u32                                         queueFamilyIndex,
-        VkSurfaceKHR                                surface,
-        VkBool32*                                   pSupported) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    return ?
-}
-
-@extension("VK_KHR_surface") // 1
-cmd VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkSurfaceKHR                                surface,
-        VkSurfaceCapabilitiesKHR*                   pSurfaceCapabilities) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    surfaceCapabilities := ?
-    pSurfaceCapabilities[0] = surfaceCapabilities
-
-    return ?
-}
-
-@extension("VK_KHR_surface") // 1
-cmd VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkSurfaceKHR                                surface,
-        u32*                                        pSurfaceFormatCount,
-        VkSurfaceFormatKHR*                         pSurfaceFormats) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    count := as!u32(?)
-    pSurfaceFormatCount[0] = count
-    surfaceFormats := pSurfaceFormats[0:count]
-
-    for i in (0 .. count) {
-        surfaceFormat := ?
-        surfaceFormats[i] = surfaceFormat
-    }
-
-    return ?
-}
-
-@extension("VK_KHR_surface") // 1
-cmd VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkSurfaceKHR                                surface,
-        u32*                                        pPresentModeCount,
-        VkPresentModeKHR*                           pPresentModes) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-
-    count := as!u32(?)
-    pPresentModeCount[0] = count
-    presentModes := pPresentModes[0:count]
-
-    for i in (0 .. count) {
-        presentMode := ?
-        presentModes[i] = presentMode
-    }
-
-    return ?
-}
-
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkCreateSwapchainKHR(
-        VkDevice                                 device,
-        const VkSwapchainCreateInfoKHR*          pCreateInfo,
-        const VkAllocationCallbacks*             pAllocator,
-        VkSwapchainKHR*                          pSwapchain) {
-    assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR)
-    deviceObject := GetDevice(device)
-
-    swapchain := ?
-    pSwapchain[0] = swapchain
-    State.Swapchains[swapchain] = new!SwapchainObject(device: device)
-
-    return ?
-}
-
-@extension("VK_KHR_swapchain") // 2
-cmd void vkDestroySwapchainKHR(
-        VkDevice                                 device,
-        VkSwapchainKHR                           swapchain,
-        const VkAllocationCallbacks*             pAllocator) {
-    deviceObject := GetDevice(device)
-    swapchainObject := GetSwapchain(swapchain)
-    assert(swapchainObject.device == device)
-
-    State.Swapchains[swapchain] = null
-}
-
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkGetSwapchainImagesKHR(
-        VkDevice                                 device,
-        VkSwapchainKHR                           swapchain,
-        u32*                                     pSwapchainImageCount,
-        VkImage*                                 pSwapchainImages) {
-    deviceObject := GetDevice(device)
-
-    count := as!u32(?)
-    pSwapchainImageCount[0] = count
-    swapchainImages := pSwapchainImages[0:count]
-
-    for i in (0 .. count) {
-        swapchainImage := ?
-        swapchainImages[i] = swapchainImage
-        State.Images[swapchainImage] = new!ImageObject(device: device)
-    }
-
-    return ?
-}
-
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkAcquireNextImageKHR(
-        VkDevice                                 device,
-        VkSwapchainKHR                           swapchain,
-        u64                                      timeout,
-        VkSemaphore                              semaphore,
-        VkFence                                  fence,
-        u32*                                     pImageIndex) {
-    deviceObject := GetDevice(device)
-    swapchainObject := GetSwapchain(swapchain)
-
-    imageIndex := ?
-    pImageIndex[0] = imageIndex
-
-    return ?
-}
-
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkQueuePresentKHR(
-        VkQueue                                  queue,
-        const VkPresentInfoKHR*                  pPresentInfo) {
-    queueObject := GetQueue(queue)
-
-    presentInfo := ?
-    pPresentInfo[0] = presentInfo
-
-    return ?
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkGetDeviceGroupPresentCapabilitiesKHR(
-        VkDevice                                    device,
-        VkDeviceGroupPresentCapabilitiesKHR*        pDeviceGroupPresentCapabilities) {
-    return ?
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkGetDeviceGroupSurfacePresentModesKHR(
-        VkDevice                                    device,
-        VkSurfaceKHR                                surface,
-        VkDeviceGroupPresentModeFlagsKHR*           pModes) {
-    return ?
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkGetPhysicalDevicePresentRectanglesKHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkSurfaceKHR                                surface,
-        u32*                                        pRectCount,
-        VkRect2D*                                   pRects) {
-    return ?
-}
-
-@vulkan1_1
-@extension("VK_KHR_swapchain") // 2
-cmd VkResult vkAcquireNextImage2KHR(
-        VkDevice                                    device,
-        const VkAcquireNextImageInfoKHR*            pAcquireInfo,
-        u32*                                        pImageIndex) {
-    return ?
-}
-
-@extension("VK_KHR_display") // 3
-cmd VkResult vkGetPhysicalDeviceDisplayPropertiesKHR(
-        VkPhysicalDevice                        physicalDevice,
-        u32*                                    pPropertyCount,
-        VkDisplayPropertiesKHR*                 pProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_display") // 3
-cmd VkResult vkGetPhysicalDeviceDisplayPlanePropertiesKHR(
-        VkPhysicalDevice                        physicalDevice,
-        u32*                                    pPropertyCount,
-        VkDisplayPlanePropertiesKHR*            pProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_display") // 3
-cmd VkResult vkGetDisplayPlaneSupportedDisplaysKHR(
-        VkPhysicalDevice                        physicalDevice,
-        u32                                     planeIndex,
-        u32*                                    pDisplayCount,
-        VkDisplayKHR*                           pDisplays) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_display") // 3
-cmd VkResult vkGetDisplayModePropertiesKHR(
-        VkPhysicalDevice                        physicalDevice,
-        VkDisplayKHR                            display,
-        u32*                                    pPropertyCount,
-        VkDisplayModePropertiesKHR*             pProperties) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_display") // 3
-cmd VkResult vkCreateDisplayModeKHR(
-        VkPhysicalDevice                        physicalDevice,
-        VkDisplayKHR                            display,
-        const VkDisplayModeCreateInfoKHR*       pCreateInfo,
-        const VkAllocationCallbacks*            pAllocator,
-        VkDisplayModeKHR*                       pMode) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_display") // 3
-cmd VkResult vkGetDisplayPlaneCapabilitiesKHR(
-        VkPhysicalDevice                        physicalDevice,
-        VkDisplayModeKHR                        mode,
-        u32                                     planeIndex,
-        VkDisplayPlaneCapabilitiesKHR*          pCapabilities) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_display") // 3
-cmd VkResult vkCreateDisplayPlaneSurfaceKHR(
-        VkInstance                              instance,
-        const VkDisplaySurfaceCreateInfoKHR*    pCreateInfo,
-        const VkAllocationCallbacks*            pAllocator,
-        VkSurfaceKHR*                           pSurface) {
-    return ?
-}
-
-@extension("VK_KHR_display_swapchain") // 4
-cmd VkResult vkCreateSharedSwapchainsKHR(
-        VkDevice                                device,
-        u32                                     swapchainCount,
-        const VkSwapchainCreateInfoKHR*         pCreateInfos,
-        const VkAllocationCallbacks*            pAllocator,
-        VkSwapchainKHR*                         pSwapchains) {
-    return ?
-}
-
-@extension("VK_KHR_xlib_surface") // 5
-cmd VkResult vkCreateXlibSurfaceKHR(
-        VkInstance                              instance,
-        const VkXlibSurfaceCreateInfoKHR*       pCreateInfo,
-        const VkAllocationCallbacks*            pAllocator,
-        VkSurfaceKHR*                           pSurface) {
-    instanceObject := GetInstance(instance)
-    return ?
-}
-
-@extension("VK_KHR_xlib_surface") // 5
-cmd VkBool32 vkGetPhysicalDeviceXlibPresentationSupportKHR(
-        VkPhysicalDevice                        physicalDevice,
-        u32                                     queueFamilyIndex,
-        platform.Display*                       dpy,
-        platform.VisualID                       visualID) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_xcb_surface") // 6
-cmd VkResult vkCreateXcbSurfaceKHR(
-        VkInstance                              instance,
-        const VkXcbSurfaceCreateInfoKHR*        pCreateInfo,
-        const VkAllocationCallbacks*            pAllocator,
-        VkSurfaceKHR*                           pSurface) {
-    instanceObject := GetInstance(instance)
-    return ?
-}
-
-@extension("VK_KHR_xcb_surface") // 6
-cmd VkBool32 vkGetPhysicalDeviceXcbPresentationSupportKHR(
-        VkPhysicalDevice                        physicalDevice,
-        u32                                     queueFamilyIndex,
-        platform.xcb_connection_t*              connection,
-        platform.xcb_visualid_t                 visual_id) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_wayland_surface") // 7
-cmd VkResult vkCreateWaylandSurfaceKHR(
-        VkInstance                              instance,
-        const VkWaylandSurfaceCreateInfoKHR*    pCreateInfo,
-        const VkAllocationCallbacks*            pAllocator,
-        VkSurfaceKHR*                           pSurface) {
-    instanceObject := GetInstance(instance)
-    return ?
-}
-
-@extension("VK_KHR_wayland_surface") // 7
-cmd VkBool32 vkGetPhysicalDeviceWaylandPresentationSupportKHR(
-        VkPhysicalDevice                        physicalDevice,
-        u32                                     queueFamilyIndex,
-        platform.wl_display*                    display) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_KHR_android_surface") // 9
-cmd VkResult vkCreateAndroidSurfaceKHR(
-        VkInstance                              instance,
-        const VkAndroidSurfaceCreateInfoKHR*    pCreateInfo,
-        const VkAllocationCallbacks*            pAllocator,
-        VkSurfaceKHR*                           pSurface) {
-    instanceObject := GetInstance(instance)
-    return ?
-}
-
-@extension("VK_KHR_win32_surface") // 10
-cmd VkResult vkCreateWin32SurfaceKHR(
-        VkInstance                              instance,
-        const VkWin32SurfaceCreateInfoKHR*      pCreateInfo,
-        const VkAllocationCallbacks*            pAllocator,
-        VkSurfaceKHR*                           pSurface) {
-    instanceObject := GetInstance(instance)
-    return ?
-}
-
-@extension("VK_KHR_win32_surface") // 10
-cmd VkResult vkGetPhysicalDeviceWin32PresentationSupportKHR(
-        VkPhysicalDevice                        physicalDevice,
-        u32                                     queueFamilyIndex) {
-    physicalDeviceObject := GetPhysicalDevice(physicalDevice)
-    return ?
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-@optional
-cmd VkResult vkGetSwapchainGrallocUsageANDROID(
-        VkDevice                                device,
-        VkFormat                                format,
-        VkImageUsageFlags                       imageUsage,
-        s32*                                    grallocUsage) {
-    return ?
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-@optional
-cmd VkResult vkGetSwapchainGrallocUsage2ANDROID(
-        VkDevice                                device,
-        VkFormat                                format,
-        VkImageUsageFlags                       imageUsage,
-        VkSwapchainImageUsageFlagsANDROID       swapchainImageUsage,
-        u64*                                    grallocConsumerUsage,
-        u64*                                    grallocProducerUsage) {
-    return ?
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-cmd VkResult vkAcquireImageANDROID(
-        VkDevice                                device,
-        VkImage                                 image,
-        int                                     nativeFenceFd,
-        VkSemaphore                             semaphore,
-        VkFence                                 fence) {
-    return ?
-}
-
-@extension("VK_ANDROID_native_buffer") // 11
-cmd VkResult vkQueueSignalReleaseImageANDROID(
-        VkQueue                                 queue,
-        u32                                     waitSemaphoreCount,
-        const VkSemaphore*                      pWaitSemaphores,
-        VkImage                                 image,
-        int*                                    pNativeFenceFd) {
-    return ?
-}
-
-@extension("VK_EXT_debug_report") // 12
-@external type void* PFN_vkDebugReportCallbackEXT
-@extension("VK_EXT_debug_report") // 12
-@pfn cmd VkBool32 vkDebugReportCallbackEXT(
-        VkDebugReportFlagsEXT                   flags,
-        VkDebugReportObjectTypeEXT              objectType,
-        u64                                     object,
-        platform.size_t                         location,
-        s32                                     messageCode,
-        const char*                             pLayerPrefix,
-        const char*                             pMessage,
-        void*                                   pUserData) {
-    return ?
-}
-
-@extension("VK_EXT_debug_report") // 12
-cmd VkResult vkCreateDebugReportCallbackEXT(
-        VkInstance                                  instance,
-        const VkDebugReportCallbackCreateInfoEXT*   pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDebugReportCallbackEXT*                   pCallback) {
-    return ?
-}
-
-@extension("VK_EXT_debug_report") // 12
-cmd void vkDestroyDebugReportCallbackEXT(
-        VkInstance                                  instance,
-        VkDebugReportCallbackEXT                    callback,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@extension("VK_EXT_debug_report") // 12
-cmd void vkDebugReportMessageEXT(
-        VkInstance                                  instance,
-        VkDebugReportFlagsEXT                       flags,
-        VkDebugReportObjectTypeEXT                  objectType,
-        u64                                         object,
-        platform.size_t                             location,
-        s32                                         messageCode,
-        const char*                                 pLayerPrefix,
-        const char*                                 pMessage) {
-}
-
-@extension("VK_EXT_debug_marker") // 23
-cmd VkResult vkDebugMarkerSetObjectTagEXT(
-        VkDevice                                    device,
-        const VkDebugMarkerObjectTagInfoEXT*        pTagInfo) {
-    return ?
-}
-
-@extension("VK_EXT_debug_marker") // 23
-cmd VkResult vkDebugMarkerSetObjectNameEXT(
-        VkDevice                                    device,
-        const VkDebugMarkerObjectNameInfoEXT*       pNameInfo) {
-    return ?
-}
-
-@extension("VK_EXT_debug_marker") // 23
-cmd void vkCmdDebugMarkerBeginEXT(
-        VkCommandBuffer                             commandBuffer,
-        const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo) {
-}
-
-@extension("VK_EXT_debug_marker") // 23
-cmd void vkCmdDebugMarkerEndEXT(
-        VkCommandBuffer                             commandBuffer) {
-}
-
-@extension("VK_EXT_debug_marker") // 23
-cmd void vkCmdDebugMarkerInsertEXT(
-        VkCommandBuffer                             commandBuffer,
-        const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo) {
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-cmd void vkCmdBindTransformFeedbackBuffersEXT(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstBinding,
-        u32                                         bindingCount,
-        const VkBuffer*                             pBuffers,
-        const VkDeviceSize*                         pOffsets,
-        const VkDeviceSize*                         pSizes) {
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-cmd void vkCmdBeginTransformFeedbackEXT(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstCounterBuffer,
-        u32                                         counterBufferCount,
-        const VkBuffer*                             pCounterBuffers,
-        const VkDeviceSize*                         pCounterBufferOffsets) {
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-cmd void vkCmdEndTransformFeedbackEXT(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstCounterBuffer,
-        u32                                         counterBufferCount,
-        const VkBuffer*                             pCounterBuffers,
-        const VkDeviceSize*                         pCounterBufferOffsets) {
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-cmd void vkCmdBeginQueryIndexedEXT(
-        VkCommandBuffer                             commandBuffer,
-        VkQueryPool                                 queryPool,
-        u32                                         query,
-        VkQueryControlFlags                         flags,
-        u32                                         index) {
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-cmd void vkCmdEndQueryIndexedEXT(
-        VkCommandBuffer                             commandBuffer,
-        VkQueryPool                                 queryPool,
-        u32                                         query,
-        u32                                         index) {
-}
-
-@extension("VK_EXT_transform_feedback") // 29
-cmd void vkCmdDrawIndirectByteCountEXT(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         instanceCount,
-        u32                                         firstInstance,
-        VkBuffer                                    counterBuffer,
-        VkDeviceSize                                counterBufferOffset,
-        u32                                         counterOffset,
-        u32                                         vertexStride) {
-}
-
-@extension("VK_AMD_draw_indirect_count") // 34
-cmd void vkCmdDrawIndirectCountAMD(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        VkBuffer                                    countBuffer,
-        VkDeviceSize                                countBufferOffset,
-        u32                                         maxDrawCount,
-        u32                                         stride) {
-}
-
-@extension("VK_AMD_draw_indirect_count") // 34
-cmd void vkCmdDrawIndexedIndirectCountAMD(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        VkBuffer                                    countBuffer,
-        VkDeviceSize                                countBufferOffset,
-        u32                                         maxDrawCount,
-        u32                                         stride) {
-}
-
-@extension("VK_AMD_shader_info") // 43
-cmd VkResult vkGetShaderInfoAMD(
-        VkDevice                                    device,
-        VkPipeline                                  pipeline,
-        VkShaderStageFlagBits                       shaderStage,
-        VkShaderInfoTypeAMD                         infoType,
-        platform.size_t*                            pInfoSize,
-        void*                                       pInfo) {
-    return ?
-}
-
-@extension("VK_NV_external_memory_capabilities") // 56
-cmd VkResult vkGetPhysicalDeviceExternalImageFormatPropertiesNV(
-        VkPhysicalDevice                            physicalDevice,
-        VkFormat                                    format,
-        VkImageType                                 type,
-        VkImageTiling                               tiling,
-        VkImageUsageFlags                           usage,
-        VkImageCreateFlags                          flags,
-        VkExternalMemoryHandleTypeFlagsNV           externalHandleType,
-        VkExternalImageFormatPropertiesNV*          pExternalImageFormatProperties) {
-    return ?
-}
-
-@extension("VK_NV_external_memory_win32") // 58
-cmd VkResult vkGetMemoryWin32HandleNV(
-        VkDevice                                    device,
-        VkDeviceMemory                              memory,
-        VkExternalMemoryHandleTypeFlagsNV           handleType,
-        platform.HANDLE*                            pHandle) {
-    return ?
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-cmd void vkGetPhysicalDeviceFeatures2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceFeatures2KHR*               pFeatures) {
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-cmd void vkGetPhysicalDeviceProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceProperties2KHR*             pProperties) {
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-cmd void vkGetPhysicalDeviceFormatProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkFormat                                    format,
-        VkFormatProperties2KHR*                     pFormatProperties) {
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-cmd VkResult vkGetPhysicalDeviceImageFormatProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceImageFormatInfo2KHR*  pImageFormatInfo,
-        VkImageFormatProperties2KHR*                pImageFormatProperties) {
-    return ?
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-cmd void vkGetPhysicalDeviceQueueFamilyProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        u32*                                        pQueueFamilyPropertyCount,
-        VkQueueFamilyProperties2KHR*                pQueueFamilyProperties) {
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-cmd void vkGetPhysicalDeviceMemoryProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkPhysicalDeviceMemoryProperties2KHR*       pMemoryProperties) {
-}
-
-@extension("VK_KHR_get_physical_device_properties2") // 60
-cmd void vkGetPhysicalDeviceSparseImageFormatProperties2KHR(
-        VkPhysicalDevice                                    physicalDevice,
-        const VkPhysicalDeviceSparseImageFormatInfo2KHR*    pFormatInfo,
-        u32*                                                pPropertyCount,
-        VkSparseImageFormatProperties2KHR*                  pProperties) {
-}
-
-@extension("VK_KHR_device_group") // 61
-cmd void vkGetDeviceGroupPeerMemoryFeaturesKHR(
-        VkDevice                                    device,
-        u32                                         heapIndex,
-        u32                                         localDeviceIndex,
-        u32                                         remoteDeviceIndex,
-        VkPeerMemoryFeatureFlagsKHR*                pPeerMemoryFeatures) {
-}
-
-@extension("VK_KHR_device_group") // 61
-cmd void vkCmdSetDeviceMaskKHR(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         deviceMask) {
-}
-
-
-@extension("VK_KHR_device_group") // 61
-cmd void vkCmdDispatchBaseKHR(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         baseGroupX,
-        u32                                         baseGroupY,
-        u32                                         baseGroupZ,
-        u32                                         groupCountX,
-        u32                                         groupCountY,
-        u32                                         groupCountZ) {
-}
-
-@extension("VK_NN_vi_surface") // 63
-cmd VkResult vkCreateViSurfaceNN(
-        VkInstance                                  instance,
-        const VkViSurfaceCreateInfoNN*              pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkSurfaceKHR*                               pSurface) {
-    return ?
-}
-
-@extension("VK_KHR_maintenance1") // 70
-cmd void vkTrimCommandPoolKHR(
-        VkDevice                                    device,
-        VkCommandPool                               commandPool,
-        VkCommandPoolTrimFlagsKHR                   flags) {
-}
-
-@extension("VK_KHR_device_group_creation") // 71
-@threadSafety("system")
-cmd VkResult vkEnumeratePhysicalDeviceGroupsKHR(
-        VkInstance                                  instance,
-        u32*                                        pPhysicalDeviceGroupCount,
-        VkPhysicalDeviceGroupPropertiesKHR*         pPhysicalDeviceGroupProperties) {
-    instanceObject := GetInstance(instance)
-
-    physicalDeviceGroupCount := as!u32(?)
-    pPhysicalDeviceGroupCount[0] = physicalDeviceGroupCount
-    physicalDevices := pPhysicalDeviceGroupProperties[0:physicalDeviceGroupCount]
-
-    for i in (0 .. physicalDeviceGroupCount) {
-        physicalDevice := ?
-        physicalDevices[i] = physicalDevice
-        if !(physicalDevice in State.PhysicalDevices) {
-            State.PhysicalDevices[physicalDevice] = new!PhysicalDeviceObject(instance: instance)
-        }
-    }
-
-    return ?
-}
-
-@extension("VK_KHR_external_memory_capabilities") // 72
-cmd void vkGetPhysicalDeviceExternalBufferPropertiesKHR(
-        VkPhysicalDevice                                physicalDevice,
-        const VkPhysicalDeviceExternalBufferInfoKHR*    pExternalBufferInfo,
-        VkExternalBufferPropertiesKHR*                  pExternalBufferProperties) {
-}
-
-@extension("VK_KHR_external_memory_win32") // 74
-cmd VkResult vkGetMemoryWin32HandleKHR(
-        VkDevice                                    device,
-        const VkMemoryGetWin32HandleInfoKHR*        pGetWin32HandleInfo,
-        platform.HANDLE*                            pHandle) {
-    return ?
-}
-
-@extension("VK_KHR_external_memory_win32") // 74
-cmd VkResult vkGetMemoryWin32HandlePropertiesKHR(
-        VkDevice                                    device,
-        VkExternalMemoryHandleTypeFlagBitsKHR       handleType,
-        platform.HANDLE                             handle,
-        VkMemoryWin32HandlePropertiesKHR*           pMemoryWin32HandleProperties) {
-    return ?
-}
-
-@extension("VK_KHR_external_memory_fd") // 75
-cmd VkResult vkGetMemoryFdKHR(
-        VkDevice                                    device,
-        const VkMemoryGetFdInfoKHR*                 pGetFdInfo,
-        s32*                                        pFd) {
-    return ?
-}
-
-@extension("VK_KHR_external_memory_fd") // 75
-cmd VkResult vkGetMemoryFdPropertiesKHR(
-        VkDevice                                    device,
-        VkExternalMemoryHandleTypeFlagBitsKHR       handleType,
-        s32                                         fd,
-        VkMemoryFdPropertiesKHR*                    pMemoryFdProperties) {
-    return ?
-}
-
-@extension("VK_KHR_external_semaphore_capabilities") // 77
-cmd void vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceExternalSemaphoreInfoKHR* pExternalSemaphoreInfo,
-        VkExternalSemaphorePropertiesKHR*           pExternalSemaphoreProperties) {
-}
-
-@extension("VK_KHR_external_semaphore_win32") // 79
-cmd VkResult vkImportSemaphoreWin32HandleKHR(
-        VkDevice                                    device,
-        const VkImportSemaphoreWin32HandleInfoKHR*  pImportSemaphoreWin32HandleInfo) {
-    return ?
-}
-
-@extension("VK_KHR_external_semaphore_win32") // 79
-cmd VkResult vkGetSemaphoreWin32HandleKHR(
-        VkDevice                                    device,
-        const VkSemaphoreGetWin32HandleInfoKHR*     pGetWin32HandleInfo,
-        platform.HANDLE*                            pHandle) {
-    return ?
-}
-
-@extension("VK_KHR_external_semaphore_fd") // 80
-cmd VkResult vkImportSemaphoreFdKHR(
-        VkDevice                                    device,
-        const VkImportSemaphoreFdInfoKHR*           pImportSemaphoreFdInfo) {
-    return ?
-}
-
-@extension("VK_KHR_external_semaphore_fd") // 80
-cmd VkResult vkGetSemaphoreFdKHR(
-        VkDevice                                    device,
-        const VkSemaphoreGetFdInfoKHR*              pGetFdInfo,
-        s32*                                        pFd) {
-    return ?
-}
-
-@extension("VK_KHR_push_descriptor") // 81
-cmd void vkCmdPushDescriptorSetKHR(
-        VkCommandBuffer                             commandBuffer,
-        VkPipelineBindPoint                         pipelineBindPoint,
-        VkPipelineLayout                            layout,
-        u32                                         set,
-        u32                                         descriptorWriteCount,
-        const VkWriteDescriptorSet*                 pDescriptorWrites) {
-}
-
-@extension("VK_EXT_conditional_rendering") // 82
-cmd void vkCmdBeginConditionalRenderingEXT(
-        VkCommandBuffer                             commandBuffer,
-        const VkConditionalRenderingBeginInfoEXT*   pConditionalRenderingBegin) {
-}
-
-@extension("VK_EXT_conditional_rendering") // 82
-cmd void vkCmdEndConditionalRenderingEXT(
-        VkCommandBuffer                             commandBuffer) {
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-cmd VkResult vkCreateDescriptorUpdateTemplateKHR(
-        VkDevice                                    device,
-        const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDescriptorUpdateTemplateKHR*              pDescriptorUpdateTemplate) {
-    return ?
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-cmd void vkDestroyDescriptorUpdateTemplateKHR(
-        VkDevice                                    device,
-        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-cmd void vkUpdateDescriptorSetWithTemplateKHR(
-        VkDevice                                    device,
-        VkDescriptorSet                             descriptorSet,
-        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
-        const void*                                 pData) {
-}
-
-@extension("VK_KHR_descriptor_update_template") // 86
-cmd void vkCmdPushDescriptorSetWithTemplateKHR(
-        VkCommandBuffer                             commandBuffer,
-        VkDescriptorUpdateTemplateKHR               descriptorUpdateTemplate,
-        VkPipelineLayout                            layout,
-        u32                                         set,
-        const void*                                 pData) {
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd void vkCmdProcessCommandsNVX(
-        VkCommandBuffer                             commandBuffer,
-        const VkCmdProcessCommandsInfoNVX*          pProcessCommandsInfo) {
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd void vkCmdReserveSpaceForCommandsNVX(
-        VkCommandBuffer                             commandBuffer,
-        const VkCmdReserveSpaceForCommandsInfoNVX*  pReserveSpaceInfo) {
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd VkResult vkCreateIndirectCommandsLayoutNVX(
-        VkDevice                                    device,
-        const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkIndirectCommandsLayoutNVX*                pIndirectCommandsLayout) {
-    return ?
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd void vkDestroyIndirectCommandsLayoutNVX(
-        VkDevice                                    device,
-        VkIndirectCommandsLayoutNVX                 indirectCommandsLayout,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd VkResult vkCreateObjectTableNVX(
-        VkDevice                                    device,
-        const VkObjectTableCreateInfoNVX*           pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkObjectTableNVX*                           pObjectTable) {
-    return ?
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd void vkDestroyObjectTableNVX(
-        VkDevice                                    device,
-        VkObjectTableNVX                            objectTable,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd VkResult vkRegisterObjectsNVX(
-        VkDevice                                    device,
-        VkObjectTableNVX                            objectTable,
-        u32                                         objectCount,
-        const VkObjectTableEntryNVX* const*         ppObjectTableEntries,
-        const u32*                                  pObjectIndices) {
-    return ?
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd VkResult vkUnregisterObjectsNVX(
-        VkDevice                                    device,
-        VkObjectTableNVX                            objectTable,
-        u32                                         objectCount,
-        const VkObjectEntryTypeNVX*                 pObjectEntryTypes,
-        const u32*                                  pObjectIndices) {
-    return ?
-}
-
-@extension("VK_NVX_device_generated_commands") // 87
-cmd void vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX(
-        VkPhysicalDevice                            physicalDevice,
-        VkDeviceGeneratedCommandsFeaturesNVX*       pFeatures,
-        VkDeviceGeneratedCommandsLimitsNVX*         pLimits) {
-}
-
-@extension("VK_NV_clip_space_w_scaling") // 88
-cmd void vkCmdSetViewportWScalingNV(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstViewport,
-        u32                                         viewportCount,
-        const VkViewportWScalingNV*                 pViewportWScalings) {
-}
-
-@extension("VK_EXT_direct_mode_display") // 89
-cmd VkResult vkReleaseDisplayEXT(
-        VkPhysicalDevice                            physicalDevice,
-        VkDisplayKHR                                display) {
-    return ?
-}
-
-@extension("VK_EXT_acquire_xlib_display") // 90
-cmd VkResult vkAcquireXlibDisplayEXT(
-        VkPhysicalDevice                            physicalDevice,
-        platform.Display*                           dpy,
-        VkDisplayKHR                                display) {
-    return ?
-}
-
-@extension("VK_EXT_acquire_xlib_display") // 90
-cmd VkResult vkGetRandROutputDisplayEXT(
-        VkPhysicalDevice                            physicalDevice,
-        platform.Display*                           dpy,
-        platform.RROutput                           rrOutput,
-        VkDisplayKHR*                               pDisplay) {
-    return ?
-}
-
-@extension("VK_EXT_display_surface_counter") // 91
-cmd VkResult vkGetPhysicalDeviceSurfaceCapabilities2EXT(
-        VkPhysicalDevice                            physicalDevice,
-        VkSurfaceKHR                                surface,
-        VkSurfaceCapabilities2EXT*                  pSurfaceCapabilities) {
-    return ?
-}
-
-@extension("VK_EXT_display_control") // 92
-cmd VkResult vkDisplayPowerControlEXT(
-        VkDevice                                    device,
-        VkDisplayKHR                                display,
-        const VkDisplayPowerInfoEXT*                pDisplayPowerInfo) {
-    return ?
-}
-
-@extension("VK_EXT_display_control") // 92
-cmd VkResult vkRegisterDeviceEventEXT(
-        VkDevice                                    device,
-        const VkDeviceEventInfoEXT*                 pDeviceEventInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkFence*                                    pFence) {
-    return ?
-}
-
-@extension("VK_EXT_display_control") // 92
-cmd VkResult vkRegisterDisplayEventEXT(
-        VkDevice                                    device,
-        VkDisplayKHR                                display,
-        const VkDisplayEventInfoEXT*                pDisplayEventInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkFence*                                    pFence) {
-    return ?
-}
-
-@extension("VK_EXT_display_control") // 92
-cmd VkResult vkGetSwapchainCounterEXT(
-        VkDevice                                    device,
-        VkSwapchainKHR                              swapchain,
-        VkSurfaceCounterFlagBitsEXT                 counter,
-        u64*                                        pCounterValue) {
-    return ?
-}
-
-@extension("VK_GOOGLE_display_timing") // 93
-cmd VkResult vkGetRefreshCycleDurationGOOGLE(
-        VkDevice                                    device,
-        VkSwapchainKHR                              swapchain,
-        VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties) {
-    deviceObject := GetDevice(device)
-    swapchainObject := GetSwapchain(swapchain)
-
-    displayTimingProperties := ?
-    pDisplayTimingProperties[0] = displayTimingProperties
-
-    return ?
-}
-
-@extension("VK_GOOGLE_display_timing") // 93
-cmd VkResult vkGetPastPresentationTimingGOOGLE(
-        VkDevice                                    device,
-        VkSwapchainKHR                              swapchain,
-        u32*                                        pPresentationTimingCount,
-        VkPastPresentationTimingGOOGLE*             pPresentationTimings) {
-    return ?
-}
-
-@extension("VK_EXT_discard_rectangles") // 100
-cmd void vkCmdSetDiscardRectangleEXT(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstDiscardRectangle,
-        u32                                         discardRectangleCount,
-        const VkRect2D*                             pDiscardRectangles) {
-}
-
-@extension("VK_EXT_hdr_metadata") // 106
-cmd void vkSetHdrMetadataEXT(
-        VkDevice                                    device,
-        u32                                         swapchainCount,
-        const VkSwapchainKHR*                       pSwapchains,
-        const VkHdrMetadataEXT*                     pMetadata) {
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-cmd VkResult vkCreateRenderPass2KHR(
-        VkDevice                                    device,
-        const VkRenderPassCreateInfo2KHR*           pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkRenderPass*                               pRenderPass) {
-    return ?
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-cmd void vkCmdBeginRenderPass2KHR(
-        VkCommandBuffer                             commandBuffer,
-        const VkRenderPassBeginInfo*                pRenderPassBegin,
-        const VkSubpassBeginInfoKHR*                pSubpassBeginInfo) {
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-cmd void vkCmdNextSubpass2KHR(
-        VkCommandBuffer                             commandBuffer,
-        const VkSubpassBeginInfoKHR*                pSubpassBeginInfo,
-        const VkSubpassEndInfoKHR*                  pSubpassEndInfo) {
-}
-
-@extension("VK_KHR_create_renderpass2") // 110
-cmd void vkCmdEndRenderPass2KHR(
-        VkCommandBuffer                             commandBuffer,
-        const VkSubpassEndInfoKHR*                  pSubpassEndInfo) {
-}
-
-@extension("VK_KHR_shared_presentable_image") // 112
-cmd VkResult vkGetSwapchainStatusKHR(
-        VkDevice                                    device,
-        VkSwapchainKHR                              swapchain) {
-    return ?
-}
-
-@extension("VK_KHR_external_fence_capabilities") // 113
-cmd void vkGetPhysicalDeviceExternalFencePropertiesKHR(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceExternalFenceInfoKHR* pExternalFenceInfo,
-        VkExternalFencePropertiesKHR*               pExternalFenceProperties) {
-}
-
-@extension("VK_KHR_external_fence_win32") // 115
-cmd VkResult vkImportFenceWin32HandleKHR(
-        VkDevice                                    device,
-        const VkImportFenceWin32HandleInfoKHR*      pImportFenceWin32HandleInfo) {
-    return ?
-}
-
-@extension("VK_KHR_external_fence_win32") // 115
-cmd VkResult vkGetFenceWin32HandleKHR(
-        VkDevice                                    device,
-        const VkFenceGetWin32HandleInfoKHR*         pGetWin32HandleInfo,
-        platform.HANDLE*                            pHandle) {
-    return ?
-}
-
-@extension("VK_KHR_external_fence_fd") // 116
-cmd VkResult vkImportFenceFdKHR(
-        VkDevice                                    device,
-        const VkImportFenceFdInfoKHR*               pImportFenceFdInfo) {
-    return ?
-}
-
-@extension("VK_KHR_external_fence_fd") // 116
-cmd VkResult vkGetFenceFdKHR(
-        VkDevice                                    device,
-        const VkFenceGetFdInfoKHR*                  pGetFdInfo,
-        int*                                        pFd) {
-    return ?
-}
-
-@extension("VK_KHR_get_surface_capabilities2") // 120
-cmd VkResult vkGetPhysicalDeviceSurfaceCapabilities2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceSurfaceInfo2KHR*      pSurfaceInfo,
-        VkSurfaceCapabilities2KHR*                  pSurfaceCapabilities) {
-    return ?
-}
-
-@extension("VK_KHR_get_surface_capabilities2") // 120
-cmd VkResult vkGetPhysicalDeviceSurfaceFormats2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        const VkPhysicalDeviceSurfaceInfo2KHR*      pSurfaceInfo,
-        u32*                                        pSurfaceFormatCount,
-        VkSurfaceFormat2KHR*                        pSurfaceFormats) {
-    return ?
-}
-
-@extension("VK_KHR_display_properties2") // 122
-cmd VkResult vkGetPhysicalDeviceDisplayProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        u32*                                        pPropertyCount,
-        VkDisplayProperties2KHR*                    pProperties) {
-    return ?
-}
-
-@extension("VK_KHR_display_properties2") // 122
-cmd VkResult vkGetPhysicalDeviceDisplayPlaneProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        u32*                                        pPropertyCount,
-        VkDisplayPlaneProperties2KHR*               pProperties) {
-    return ?
-}
-
-@extension("VK_KHR_display_properties2") // 122
-cmd VkResult vkGetDisplayModeProperties2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        VkDisplayKHR                                display,
-        u32*                                        pPropertyCount,
-        VkDisplayModeProperties2KHR*                pProperties) {
-    return ?
-}
-
-@extension("VK_KHR_display_properties2") // 122
-cmd VkResult vkGetDisplayPlaneCapabilities2KHR(
-        VkPhysicalDevice                            physicalDevice,
-        const VkDisplayPlaneInfo2KHR*               pDisplayPlaneInfo,
-        VkDisplayPlaneCapabilities2KHR*             pCapabilities) {
-    return ?
-}
-
-@extension("VK_MVK_ios_surface") // 123
-cmd VkResult vkCreateIOSSurfaceMVK(
-        VkInstance                                  instance,
-        const VkIOSSurfaceCreateInfoMVK*            pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkSurfaceKHR*                               pSurface) {
-    return ?
-}
-
-@extension("VK_MVK_macos_surface") // 124
-cmd VkResult vkCreateMacOSSurfaceMVK(
-        VkInstance                                  instance,
-        const VkMacOSSurfaceCreateInfoMVK*          pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkSurfaceKHR*                               pSurface) {
-    return ?
-}
-
-@extension("VK_EXT_debug_utils") // 129
-@external type void* PFN_vkDebugUtilsMessengerCallbackEXT
-@extension("VK_EXT_debug_utils") // 129
-@pfn cmd VkBool32 vkDebugUtilsMessengerCallbackEXT(
-        VkDebugUtilsMessageSeverityFlagBitsEXT      messageSeverity,
-        VkDebugUtilsMessageTypeFlagsEXT             messageType,
-        const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
-        void*                                       pUserData) {
-    return ?
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd VkResult vkSetDebugUtilsObjectNameEXT(
-        VkDevice                                    device,
-        const VkDebugUtilsObjectNameInfoEXT*        pNameInfo) {
-    return ?
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd VkResult vkSetDebugUtilsObjectTagEXT(
-        VkDevice                                    device,
-        const VkDebugUtilsObjectTagInfoEXT*         pTagInfo) {
-    return ?
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkQueueBeginDebugUtilsLabelEXT(
-        VkQueue                                     queue,
-        const VkDebugUtilsLabelEXT*                 pLabelInfo) {
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkQueueEndDebugUtilsLabelEXT(VkQueue       queue) {
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkQueueInsertDebugUtilsLabelEXT(
-        VkQueue                                     queue,
-        const VkDebugUtilsLabelEXT*                 pLabelInfo) {
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkCmdBeginDebugUtilsLabelEXT(
-        VkCommandBuffer                             commandBuffer,
-        const VkDebugUtilsLabelEXT*                 pLabelInfo) {
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) {
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkCmdInsertDebugUtilsLabelEXT(
-        VkCommandBuffer                             commandBuffer,
-        const VkDebugUtilsLabelEXT*                 pLabelInfo) {
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd VkResult vkCreateDebugUtilsMessengerEXT(
-        VkInstance                                  instance,
-        const VkDebugUtilsMessengerCreateInfoEXT*   pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkDebugUtilsMessengerEXT*                   pMessenger) {
-    return ?
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkDestroyDebugUtilsMessengerEXT(
-        VkInstance                                  instance,
-        VkDebugUtilsMessengerEXT                    messenger,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@extension("VK_EXT_debug_utils") // 129
-cmd void vkSubmitDebugUtilsMessageEXT(
-        VkInstance                                  instance,
-        VkDebugUtilsMessageSeverityFlagBitsEXT      messageSeverity,
-        VkDebugUtilsMessageTypeFlagsEXT             messageTypes,
-        const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) {
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-@vulkan1_1 // extension requires 1.1, and should become non-optional when 1.1 does
-cmd VkResult vkGetAndroidHardwareBufferPropertiesANDROID(
-        VkDevice                                    device,
-        const platform.AHardwareBuffer*             buffer,
-        VkAndroidHardwareBufferPropertiesANDROID*   pProperties) {
-    return ?
-}
-
-@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130
-@vulkan1_1 // extension requires 1.1, and should become non-optional when 1.1 does
-cmd VkResult vkGetMemoryAndroidHardwareBufferANDROID(
-        VkDevice                                            device,
-        const VkMemoryGetAndroidHardwareBufferInfoANDROID*  pInfo,
-        platform.AHardwareBuffer**                          pBuffer) {
-    return ?
-}
-
-@extension("VK_EXT_sample_locations") // 144
-cmd void vkCmdSetSampleLocationsEXT(
-        VkCommandBuffer                             commandBuffer,
-        const VkSampleLocationsInfoEXT*             pSampleLocationsInfo) {
-}
-
-@extension("VK_EXT_sample_locations") // 144
-cmd void vkGetPhysicalDeviceMultisamplePropertiesEXT(
-        VkPhysicalDevice                            physicalDevice,
-        VkSampleCountFlagBits                       samples,
-        VkMultisamplePropertiesEXT*                 pMultisampleProperties) {
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-cmd void vkGetImageMemoryRequirements2KHR(
-        VkDevice                                    device,
-        const VkImageMemoryRequirementsInfo2KHR*    pInfo,
-        VkMemoryRequirements2KHR*                   pMemoryRequirements) {
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-cmd void vkGetBufferMemoryRequirements2KHR(
-        VkDevice                                    device,
-        const VkBufferMemoryRequirementsInfo2KHR*   pInfo,
-        VkMemoryRequirements2KHR*                   pMemoryRequirements) {
-}
-
-@extension("VK_KHR_get_memory_requirements2") // 147
-cmd void vkGetImageSparseMemoryRequirements2KHR(
-        VkDevice                                        device,
-        const VkImageSparseMemoryRequirementsInfo2KHR*  pInfo,
-        u32*                                            pSparseMemoryRequirementCount,
-        VkSparseImageMemoryRequirements2KHR*            pSparseMemoryRequirements) {
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-cmd VkResult vkCreateSamplerYcbcrConversionKHR(
-        VkDevice                                        device,
-        const VkSamplerYcbcrConversionCreateInfoKHR*    pCreateInfo,
-        const VkAllocationCallbacks*                    pAllocator,
-        VkSamplerYcbcrConversionKHR*                    pYcbcrConversion) {
-    return ?
-}
-
-@extension("VK_KHR_sampler_ycbcr_conversion") // 157
-cmd void vkDestroySamplerYcbcrConversionKHR(
-        VkDevice                                        device,
-        VkSamplerYcbcrConversionKHR                     ycbcrConversion,
-        const VkAllocationCallbacks*                    pAllocator) {
-}
-
-@extension("VK_KHR_bind_memory2") // 158
-cmd VkResult vkBindBufferMemory2KHR(
-        VkDevice                                        device,
-        u32                                             bindInfoCount,
-        const VkBindBufferMemoryInfoKHR*                pBindInfos) {
-    return ?
-}
-
-@extension("VK_KHR_bind_memory2") // 158
-cmd VkResult vkBindImageMemory2KHR(
-        VkDevice                                        device,
-        u32                                             bindInfoCount,
-        const VkBindImageMemoryInfoKHR*                 pBindInfos) {
-    return ?
-}
-
-@extension("VK_EXT_image_drm_format_modifier") // 159
-cmd VkResult vkGetImageDrmFormatModifierPropertiesEXT(
-        VkDevice                                        device,
-        VkImage                                         image,
-        VkImageDrmFormatModifierPropertiesEXT*          pProperties) {
-    return ?
-}
-
-@extension("VK_EXT_validation_cache") // 161
-cmd VkResult vkCreateValidationCacheEXT(
-        VkDevice                                    device,
-        const VkValidationCacheCreateInfoEXT*       pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkValidationCacheEXT*                       pValidationCache) {
-    return ?
-}
-
-@extension("VK_EXT_validation_cache") // 161
-cmd void vkDestroyValidationCacheEXT(
-        VkDevice                                    device,
-        VkValidationCacheEXT                        validationCache,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@extension("VK_EXT_validation_cache") // 161
-cmd VkResult vkMergeValidationCachesEXT(
-        VkDevice                                    device,
-        VkValidationCacheEXT                        dstCache,
-        u32                                         srcCacheCount,
-        const VkValidationCacheEXT*                 pSrcCaches) {
-    return ?
-}
-
-@extension("VK_EXT_validation_cache") // 161
-cmd VkResult vkGetValidationCacheDataEXT(
-        VkDevice                                    device,
-        VkValidationCacheEXT                        validationCache,
-        platform.size_t*                            pDataSize,
-        void*                                       pData) {
-    return ?
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-cmd void vkCmdBindShadingRateImageNV(
-        VkCommandBuffer                             commandBuffer,
-        VkImageView                                 imageView,
-        VkImageLayout                               imageLayout) {
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-cmd void vkCmdSetViewportShadingRatePaletteNV(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstViewport,
-        u32                                         viewportCount,
-        const VkShadingRatePaletteNV*               pShadingRatePalettes) {
-}
-
-@extension("VK_NV_shading_rate_image") // 165
-cmd void vkCmdSetCoarseSampleOrderNV(
-        VkCommandBuffer                             commandBuffer,
-        VkCoarseSampleOrderTypeNV                   sampleOrderType,
-        u32                                         customSampleOrderCount,
-        const VkCoarseSampleOrderCustomNV*          pCustomSampleOrders) {
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd VkResult vkCreateAccelerationStructureNV(
-        VkDevice                                    device,
-        const VkAccelerationStructureCreateInfoNV*  pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkAccelerationStructureNV*                  pAccelerationStructure) {
-    return ?
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd void vkDestroyAccelerationStructureNV(
-        VkDevice                                    device,
-        VkAccelerationStructureNV                   accelerationStructure,
-        const VkAllocationCallbacks*                pAllocator) {
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd void vkGetAccelerationStructureMemoryRequirementsNV(
-        VkDevice                                                device,
-        const VkAccelerationStructureMemoryRequirementsInfoNV*  pInfo,
-        VkMemoryRequirements2KHR*                               pMemoryRequirements) {
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd VkResult vkBindAccelerationStructureMemoryNV(
-        VkDevice                                        device,
-        u32                                             bindInfoCount,
-        const VkBindAccelerationStructureMemoryInfoNV*  pBindInfos) {
-    return ?
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd void vkCmdBuildAccelerationStructureNV(
-        VkCommandBuffer                             commandBuffer,
-        const VkAccelerationStructureInfoNV*        pInfo,
-        VkBuffer                                    instanceData,
-        VkDeviceSize                                instanceOffset,
-        VkBool32                                    update,
-        VkAccelerationStructureNV                   dst,
-        VkAccelerationStructureNV                   src,
-        VkBuffer                                    scratch,
-        VkDeviceSize                                scratchOffset) {
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd void vkCmdCopyAccelerationStructureNV(
-        VkCommandBuffer                             commandBuffer,
-        VkAccelerationStructureNV                   dst,
-        VkAccelerationStructureNV                   src,
-        VkCopyAccelerationStructureModeNV           mode) {
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd void vkCmdTraceRaysNV(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    raygenShaderBindingTableBuffer,
-        VkDeviceSize                                raygenShaderBindingOffset,
-        VkBuffer                                    missShaderBindingTableBuffer,
-        VkDeviceSize                                missShaderBindingOffset,
-        VkDeviceSize                                missShaderBindingStride,
-        VkBuffer                                    hitShaderBindingTableBuffer,
-        VkDeviceSize                                hitShaderBindingOffset,
-        VkDeviceSize                                hitShaderBindingStride,
-        VkBuffer                                    callableShaderBindingTableBuffer,
-        VkDeviceSize                                callableShaderBindingOffset,
-        VkDeviceSize                                callableShaderBindingStride,
-        u32                                         width,
-        u32                                         height,
-        u32                                         depth) {
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd VkResult vkCreateRaytracingPipelinesNV(
-        VkDevice                                    device,
-        VkPipelineCache                             pipelineCache,
-        u32                                         createInfoCount,
-        const VkRayTracingPipelineCreateInfoNV*     pCreateInfos,
-        const VkAllocationCallbacks*                pAllocator,
-        VkPipeline*                                 pPipelines) {
-    return ?
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd VkResult vkGetRaytracingShaderHandlesNV(
-        VkDevice                                    device,
-        VkPipeline                                  pipeline,
-        u32                                         firstGroup,
-        u32                                         groupCount,
-        platform.size_t                             dataSize,
-        void*                                       pData) {
-    return ?
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd VkResult vkGetAccelerationStructureHandleNV(
-        VkDevice                                    device,
-        VkAccelerationStructureNV                   accelerationStructure,
-        platform.size_t                             dataSize,
-        void*                                       pData) {
-    return ?
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd void vkCmdWriteAccelerationStructurePropertiesNV(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         accelerationStructureCount,
-        const VkAccelerationStructureNV*            pAccelerationStructures,
-        VkQueryType                                 queryType,
-        VkQueryPool                                 queryPool,
-        u32                                         firstQuery) {
-}
-
-@extension("VK_NV_ray_tracing") // 166
-cmd VkResult vkCompileDeferredNV(
-        VkDevice                                    device,
-        VkPipeline                                  pipeline,
-        u32                                         shader) {
-    return ?
-}
-
-@extension("VK_KHR_maintenance3") // 169
-cmd void vkGetDescriptorSetLayoutSupportKHR(
-        VkDevice                                    device,
-        const VkDescriptorSetLayoutCreateInfo*      pCreateInfo,
-        VkDescriptorSetLayoutSupportKHR*            pSupport) {
-}
-
-@extension("VK_KHR_draw_indirect_count") // 170
-cmd void vkCmdDrawIndirectCountKHR(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        VkBuffer                                    countBuffer,
-        VkDeviceSize                                countBufferOffset,
-        u32                                         maxDrawCount,
-        u32                                         stride) {
-}
-
-@extension("VK_KHR_draw_indirect_count") // 170
-cmd void vkCmdDrawIndexedIndirectCountKHR(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        VkBuffer                                    countBuffer,
-        VkDeviceSize                                countBufferOffset,
-        u32                                         maxDrawCount,
-        u32                                         stride) {
-}
-
-@extension("VK_EXT_external_memory_host") // 179
-cmd VkResult vkGetMemoryHostPointerPropertiesEXT(
-        VkDevice                                    device,
-        VkExternalMemoryHandleTypeFlagBits          handleType,
-        const void*                                 pHostPointer,
-        VkMemoryHostPointerPropertiesEXT*           pMemoryHostPointerProperties) {
-    return ?
-}
-
-@extension("VK_AMD_buffer_marker") // 180
-cmd void vkCmdWriteBufferMarkerAMD(
-        VkCommandBuffer                             commandBuffer,
-        VkPipelineStageFlagBits                     pipelineStage,
-        VkBuffer                                    dstBuffer,
-        VkDeviceSize                                dstOffset,
-        u32                                         marker) {
-}
-
-@extension("VK_EXT_calibrated_timestamps") // 185
-cmd VkResult vkGetPhysicalDeviceCalibrateableTimeDomainsEXT(
-        VkPhysicalDevice                            physicalDevice,
-        u32*                                        pTimeDomainCount,
-        VkTimeDomainEXT*                            pTimeDomains) {
-    return ?
-}
-
-@extension("VK_EXT_calibrated_timestamps") // 185
-cmd VkResult vkGetCalibratedTimestampsEXT(
-        VkDevice                                    device,
-        u32                                         timestampCount,
-        const VkCalibratedTimestampInfoEXT*         pTimestampInfos,
-        u64*                                        pTimestamps,
-        u64*                                        pMaxDeviation) {
-    return ?
-}
-
-@extension("VK_NV_mesh_shader") // 203
-cmd void vkCmdDrawMeshTasksNV(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         taskCount,
-        u32                                         firstTask) {
-}
-
-@extension("VK_NV_mesh_shader") // 203
-cmd void vkCmdDrawMeshTasksIndirectNV(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        u32                                         drawCount,
-        u32                                         stride) {
-}
-
-@extension("VK_NV_mesh_shader") // 203
-cmd void vkCmdDrawMeshTasksIndirectCountNV(
-        VkCommandBuffer                             commandBuffer,
-        VkBuffer                                    buffer,
-        VkDeviceSize                                offset,
-        VkBuffer                                    countBuffer,
-        VkDeviceSize                                countBufferOffset,
-        u32                                         maxDrawCount,
-        u32                                         stride) {
-}
-
-@extension("VK_NV_scissor_exclusive") // 206
-cmd void vkCmdSetExclusiveScissorNV(
-        VkCommandBuffer                             commandBuffer,
-        u32                                         firstExclusiveScissor,
-        u32                                         exclusiveScissorCount,
-        const VkRect2D*                             pExclusiveScissors) {
-}
-
-@extension("VK_NV_device_diagnostic_checkpoints") // 207
-cmd void vkCmdSetCheckpointNV(
-        VkCommandBuffer                             commandBuffer,
-        const void*                                 pCheckpointMarker) {
-}
-
-@extension("VK_NV_device_diagnostic_checkpoints") // 207
-cmd void vkGetQueueCheckpointDataNV(
-        VkQueue                                     queue,
-        u32*                                        pCheckpointDataCount,
-        VkCheckpointDataNV*                         pCheckpointData) {
-}
-
-@extension("VK_FUCHSIA_imagepipe_surface") // 215
-cmd VkResult vkCreateImagePipeSurfaceFUCHSIA(
-        VkInstance                                  instance,
-        const VkImagePipeSurfaceCreateInfoFUCHSIA*  pCreateInfo,
-        const VkAllocationCallbacks*                pAllocator,
-        VkSurfaceKHR*                               pSurface) {
-    return ?
-}
-
-
-////////////////
-// Validation //
-////////////////
-
-extern void validate(string layerName, bool condition, string message)
-
-
-/////////////////////////////
-// Internal State Tracking //
-/////////////////////////////
-
-StateObject State
-
-@internal class StateObject {
-    // Dispatchable objects.
-    map!(VkInstance,       ref!InstanceObject)       Instances
-    map!(VkPhysicalDevice, ref!PhysicalDeviceObject) PhysicalDevices
-    map!(VkDevice,         ref!DeviceObject)         Devices
-    map!(VkQueue,          ref!QueueObject)          Queues
-    map!(VkCommandBuffer,  ref!CommandBufferObject)  CommandBuffers
-
-    // Non-dispatchable objects.
-    map!(VkDeviceMemory,             ref!DeviceMemoryObject)             DeviceMemories
-    map!(VkBuffer,                   ref!BufferObject)                   Buffers
-    map!(VkBufferView,               ref!BufferViewObject)               BufferViews
-    map!(VkImage,                    ref!ImageObject)                    Images
-    map!(VkImageView,                ref!ImageViewObject)                ImageViews
-    map!(VkShaderModule,             ref!ShaderModuleObject)             ShaderModules
-    map!(VkPipeline,                 ref!PipelineObject)                 Pipelines
-    map!(VkPipelineLayout,           ref!PipelineLayoutObject)           PipelineLayouts
-    map!(VkSampler,                  ref!SamplerObject)                  Samplers
-    map!(VkDescriptorSet,            ref!DescriptorSetObject)            DescriptorSets
-    map!(VkDescriptorSetLayout,      ref!DescriptorSetLayoutObject)      DescriptorSetLayouts
-    map!(VkDescriptorPool,           ref!DescriptorPoolObject)           DescriptorPools
-    map!(VkFence,                    ref!FenceObject)                    Fences
-    map!(VkSemaphore,                ref!SemaphoreObject)                Semaphores
-    map!(VkEvent,                    ref!EventObject)                    Events
-    map!(VkQueryPool,                ref!QueryPoolObject)                QueryPools
-    map!(VkFramebuffer,              ref!FramebufferObject)              Framebuffers
-    map!(VkRenderPass,               ref!RenderPassObject)               RenderPasses
-    map!(VkPipelineCache,            ref!PipelineCacheObject)            PipelineCaches
-    map!(VkCommandPool,              ref!CommandPoolObject)              CommandPools
-    map!(VkSurfaceKHR,               ref!SurfaceObject)                  Surfaces
-    map!(VkSwapchainKHR,             ref!SwapchainObject)                Swapchains
-}
-
-@internal class InstanceObject {
-}
-
-@internal class PhysicalDeviceObject {
-    VkInstance instance
-}
-
-@internal class DeviceObject {
-    VkPhysicalDevice physicalDevice
-}
-
-@internal class QueueObject {
-    VkDevice      device
-    VkQueueFlags  flags
-}
-
-@internal class CommandBufferObject {
-    VkDevice                  device
-    map!(u64, VkDeviceMemory) boundObjects
-    VkQueueFlags              queueFlags
-}
-
-@internal class DeviceMemoryObject {
-    VkDevice                                device
-    VkDeviceSize                            allocationSize
-    map!(u64, VkDeviceSize)                 boundObjects
-    map!(VkCommandBuffer, VkCommandBuffer)  boundCommandBuffers
-}
-
-@internal class BufferObject {
-    VkDevice              device
-    VkDeviceMemory        memory
-    VkDeviceSize          memoryOffset
-}
-
-@internal class BufferViewObject {
-    VkDevice      device
-    VkBuffer      buffer
-}
-
-@internal class ImageObject {
-    VkDevice              device
-    VkDeviceMemory        memory
-    VkDeviceSize          memoryOffset
-}
-
-@internal class ImageViewObject {
-    VkDevice      device
-    VkImage       image
-}
-
-@internal class ShaderObject {
-    VkDevice      device
-}
-
-@internal class ShaderModuleObject {
-    VkDevice      device
-}
-
-@internal class PipelineObject {
-    VkDevice      device
-}
-
-@internal class PipelineLayoutObject {
-    VkDevice      device
-}
-
-@internal class SamplerObject {
-    VkDevice      device
-}
-
-@internal class DescriptorSetObject {
-    VkDevice      device
-}
-
-@internal class DescriptorSetLayoutObject {
-    VkDevice      device
-}
-
-@internal class DescriptorPoolObject {
-    VkDevice      device
-}
-
-@internal class FenceObject {
-    VkDevice      device
-    bool          signaled
-}
-
-@internal class SemaphoreObject {
-    VkDevice      device
-}
-
-@internal class EventObject {
-    VkDevice      device
-}
-
-@internal class QueryPoolObject {
-    VkDevice      device
-}
-
-@internal class FramebufferObject {
-    VkDevice      device
-}
-
-@internal class RenderPassObject {
-    VkDevice      device
-}
-
-@internal class PipelineCacheObject {
-    VkDevice      device
-}
-
-@internal class CommandPoolObject {
-    VkDevice      device
-}
-
-@internal class SurfaceObject {
-    VkInstance    instance
-}
-
-@internal class SwapchainObject {
-    VkDevice      device
-}
-
-macro ref!InstanceObject GetInstance(VkInstance instance) {
-    assert(instance in State.Instances)
-    return State.Instances[instance]
-}
-
-macro ref!PhysicalDeviceObject GetPhysicalDevice(VkPhysicalDevice physicalDevice) {
-    assert(physicalDevice in State.PhysicalDevices)
-    return State.PhysicalDevices[physicalDevice]
-}
-
-macro ref!DeviceObject GetDevice(VkDevice device) {
-    assert(device in State.Devices)
-    return State.Devices[device]
-}
-
-macro ref!QueueObject GetQueue(VkQueue queue) {
-    assert(queue in State.Queues)
-    return State.Queues[queue]
-}
-
-macro ref!CommandBufferObject GetCommandBuffer(VkCommandBuffer commandBuffer) {
-    assert(commandBuffer in State.CommandBuffers)
-    return State.CommandBuffers[commandBuffer]
-}
-
-macro ref!DeviceMemoryObject GetDeviceMemory(VkDeviceMemory memory) {
-    assert(memory in State.DeviceMemories)
-    return State.DeviceMemories[memory]
-}
-
-macro ref!BufferObject GetBuffer(VkBuffer buffer) {
-    assert(buffer in State.Buffers)
-    return State.Buffers[buffer]
-}
-
-macro ref!BufferViewObject GetBufferView(VkBufferView bufferView) {
-    assert(bufferView in State.BufferViews)
-    return State.BufferViews[bufferView]
-}
-
-macro ref!ImageObject GetImage(VkImage image) {
-    assert(image in State.Images)
-    return State.Images[image]
-}
-
-macro ref!ImageViewObject GetImageView(VkImageView imageView) {
-    assert(imageView in State.ImageViews)
-    return State.ImageViews[imageView]
-}
-
-macro ref!ShaderModuleObject GetShaderModule(VkShaderModule shaderModule) {
-    assert(shaderModule in State.ShaderModules)
-    return State.ShaderModules[shaderModule]
-}
-
-macro ref!PipelineObject GetPipeline(VkPipeline pipeline) {
-    assert(pipeline in State.Pipelines)
-    return State.Pipelines[pipeline]
-}
-
-macro ref!PipelineLayoutObject GetPipelineLayout(VkPipelineLayout pipelineLayout) {
-    assert(pipelineLayout in State.PipelineLayouts)
-    return State.PipelineLayouts[pipelineLayout]
-}
-
-macro ref!SamplerObject GetSampler(VkSampler sampler) {
-    assert(sampler in State.Samplers)
-    return State.Samplers[sampler]
-}
-
-macro ref!DescriptorSetObject GetDescriptorSet(VkDescriptorSet descriptorSet) {
-    assert(descriptorSet in State.DescriptorSets)
-    return State.DescriptorSets[descriptorSet]
-}
-
-macro ref!DescriptorSetLayoutObject GetDescriptorSetLayout(VkDescriptorSetLayout descriptorSetLayout) {
-    assert(descriptorSetLayout in State.DescriptorSetLayouts)
-    return State.DescriptorSetLayouts[descriptorSetLayout]
-}
-
-macro ref!DescriptorPoolObject GetDescriptorPool(VkDescriptorPool descriptorPool) {
-    assert(descriptorPool in State.DescriptorPools)
-    return State.DescriptorPools[descriptorPool]
-}
-
-macro ref!FenceObject GetFence(VkFence fence) {
-    assert(fence in State.Fences)
-    return State.Fences[fence]
-}
-
-macro ref!SemaphoreObject GetSemaphore(VkSemaphore semaphore) {
-    assert(semaphore in State.Semaphores)
-    return State.Semaphores[semaphore]
-}
-
-macro ref!EventObject GetEvent(VkEvent event) {
-    assert(event in State.Events)
-    return State.Events[event]
-}
-
-macro ref!QueryPoolObject GetQueryPool(VkQueryPool queryPool) {
-    assert(queryPool in State.QueryPools)
-    return State.QueryPools[queryPool]
-}
-
-macro ref!FramebufferObject GetFramebuffer(VkFramebuffer framebuffer) {
-    assert(framebuffer in State.Framebuffers)
-    return State.Framebuffers[framebuffer]
-}
-
-macro ref!RenderPassObject GetRenderPass(VkRenderPass renderPass) {
-    assert(renderPass in State.RenderPasses)
-    return State.RenderPasses[renderPass]
-}
-
-macro ref!PipelineCacheObject GetPipelineCache(VkPipelineCache pipelineCache) {
-    assert(pipelineCache in State.PipelineCaches)
-    return State.PipelineCaches[pipelineCache]
-}
-
-macro ref!CommandPoolObject GetCommandPool(VkCommandPool commandPool) {
-    assert(commandPool in State.CommandPools)
-    return State.CommandPools[commandPool]
-}
-
-macro ref!SurfaceObject GetSurface(VkSurfaceKHR surface) {
-    assert(surface in State.Surfaces)
-    return State.Surfaces[surface]
-}
-
-macro ref!SwapchainObject GetSwapchain(VkSwapchainKHR swapchain) {
-    assert(swapchain in State.Swapchains)
-    return State.Swapchains[swapchain]
-}
-
-macro VkQueueFlags AddQueueFlag(VkQueueFlags flags, VkQueueFlagBits bit) {
-    return as!VkQueueFlags(as!u32(flags) | as!u32(bit))
-}
diff --git a/vulkan/doc/README b/vulkan/doc/README
deleted file mode 100644
index d1dc2e1..0000000
--- a/vulkan/doc/README
+++ /dev/null
@@ -1,2 +0,0 @@
-The former contents of implementors_guide/ are now at
-https://source.android.com/devices/graphics/implement-vulkan
diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h
index 23006fa..9ffe83b 100644
--- a/vulkan/include/vulkan/vk_android_native_buffer.h
+++ b/vulkan/include/vulkan/vk_android_native_buffer.h
@@ -62,6 +62,11 @@
 typedef VkFlags VkSwapchainImageUsageFlagsANDROID;
 
 typedef struct {
+    uint64_t consumer;
+    uint64_t producer;
+} VkNativeBufferUsage2ANDROID;
+
+typedef struct {
     VkStructureType             sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
     const void*                 pNext;
 
@@ -73,10 +78,7 @@
     int                         format;
     int                         usage; // DEPRECATED in SPEC_VERSION 6
     // -- Added in SPEC_VERSION 6 --
-    struct {
-        uint64_t                consumer;
-        uint64_t                producer;
-    } usage2;
+    VkNativeBufferUsage2ANDROID usage2;
 } VkNativeBufferANDROID;
 
 typedef struct {
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 4d6b2be..018f200 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -77,6 +77,7 @@
 
     header_libs: [
         "hwvulkan_headers",
+        "libnativeloader-headers",
         "vulkan_headers",
     ],
     export_header_lib_headers: ["vulkan_headers"],
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index f8263db..7e988a1 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -145,7 +145,7 @@
     }
 
     void GetLayersFromSettings() {
-        // These will only be available if conditions are met in GraphicsEnvironemnt
+        // These will only be available if conditions are met in GraphicsEnvironment
         // gpu_debug_layers = layer1:layer2:layerN
         const std::string layers = android::GraphicsEnv::getInstance().getDebugLayers();
         if (!layers.empty()) {
@@ -670,6 +670,12 @@
         return VK_ERROR_LAYER_NOT_PRESENT;
     }
 
+    if (!layer.ref.GetGetInstanceProcAddr()) {
+        ALOGW("Failed to locate vkGetInstanceProcAddr in layer %s", name);
+        layer.ref.~LayerRef();
+        return VK_ERROR_LAYER_NOT_PRESENT;
+    }
+
     ALOGI("Loaded layer %s", name);
 
     return VK_SUCCESS;
@@ -1172,11 +1178,20 @@
 
     std::call_once(once_flag, []() {
         if (driver::OpenHAL()) {
-            DiscoverLayers();
             initialized = true;
         }
     });
 
+    {
+        static pid_t pid = getpid() + 1;
+        static std::mutex layer_lock;
+        std::lock_guard<std::mutex> lock(layer_lock);
+        if (pid != getpid()) {
+            pid = getpid();
+            DiscoverLayers();
+        }
+    }
+
     return initialized;
 }
 
@@ -1266,7 +1281,7 @@
         return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
     }
 
-    // TODO how about extensions from implicitly enabled layers?
+    // TODO(b/143293104): expose extensions from implicitly enabled layers
     return vulkan::driver::EnumerateInstanceExtensionProperties(
         nullptr, pPropertyCount, pProperties);
 }
@@ -1320,7 +1335,7 @@
         return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
     }
 
-    // TODO how about extensions from implicitly enabled layers?
+    // TODO(b/143293104): expose extensions from implicitly enabled layers
     const InstanceData& data = GetData(physicalDevice);
     return data.dispatch.EnumerateDeviceExtensionProperties(
         physicalDevice, nullptr, pPropertyCount, pProperties);
diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp
index df86af0..37b5368 100644
--- a/vulkan/libvulkan/api_gen.cpp
+++ b/vulkan/libvulkan/api_gen.cpp
@@ -16,12 +16,11 @@
 
 // WARNING: This file is generated. See ../README.md for instructions.
 
+#include <log/log.h>
 #include <string.h>
 
 #include <algorithm>
 
-#include <log/log.h>
-
 // to catch mismatches between vulkan.h and this file
 #undef VK_NO_PROTOTYPES
 #include "api.h"
@@ -55,6 +54,11 @@
 
 // clang-format off
 
+VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
+    driver::Logger(instance).Err(instance, "VK_KHR_android_surface not enabled. Exported vkCreateAndroidSurfaceKHR not executed.");
+    return VK_SUCCESS;
+}
+
 VKAPI_ATTR void disabledDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR, const VkAllocationCallbacks*) {
     driver::Logger(instance).Err(instance, "VK_KHR_surface not enabled. Exported vkDestroySurfaceKHR not executed.");
 }
@@ -113,18 +117,13 @@
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR, uint32_t*, VkRect2D*) {
-    driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_swapchain not enabled. Exported vkGetPhysicalDevicePresentRectanglesKHR not executed.");
-    return VK_SUCCESS;
-}
-
 VKAPI_ATTR VkResult disabledAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR*, uint32_t*) {
     driver::Logger(device).Err(device, "VK_KHR_swapchain not enabled. Exported vkAcquireNextImage2KHR not executed.");
     return VK_SUCCESS;
 }
 
-VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) {
-    driver::Logger(instance).Err(instance, "VK_KHR_android_surface not enabled. Exported vkCreateAndroidSurfaceKHR not executed.");
+VKAPI_ATTR VkResult disabledGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR, uint32_t*, VkRect2D*) {
+    driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_swapchain not enabled. Exported vkGetPhysicalDevicePresentRectanglesKHR not executed.");
     return VK_SUCCESS;
 }
 
@@ -162,7 +161,12 @@
     INIT_PROC(true, instance, CreateDevice);
     INIT_PROC(true, instance, EnumerateDeviceExtensionProperties);
     INIT_PROC(true, instance, GetPhysicalDeviceSparseImageFormatProperties);
-    INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
+    INIT_PROC_EXT(KHR_android_surface, true, instance, CreateAndroidSurfaceKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, DestroySurfaceKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceSupportKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceFormatsKHR);
+    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfacePresentModesKHR);
     INIT_PROC(false, instance, GetPhysicalDeviceFeatures2);
     INIT_PROC(false, instance, GetPhysicalDeviceProperties2);
     INIT_PROC(false, instance, GetPhysicalDeviceFormatProperties2);
@@ -171,15 +175,10 @@
     INIT_PROC(false, instance, GetPhysicalDeviceMemoryProperties2);
     INIT_PROC(false, instance, GetPhysicalDeviceSparseImageFormatProperties2);
     INIT_PROC(false, instance, GetPhysicalDeviceExternalBufferProperties);
-    INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
     INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties);
-    INIT_PROC_EXT(KHR_surface, true, instance, DestroySurfaceKHR);
-    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceSupportKHR);
-    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
-    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceFormatsKHR);
-    INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfacePresentModesKHR);
+    INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties);
+    INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
     INIT_PROC_EXT(KHR_swapchain, false, instance, GetPhysicalDevicePresentRectanglesKHR);
-    INIT_PROC_EXT(KHR_android_surface, true, instance, CreateAndroidSurfaceKHR);
     // clang-format on
 
     return success;
@@ -314,32 +313,32 @@
     INIT_PROC(true, dev, CmdNextSubpass);
     INIT_PROC(true, dev, CmdEndRenderPass);
     INIT_PROC(true, dev, CmdExecuteCommands);
-    INIT_PROC(false, dev, BindBufferMemory2);
-    INIT_PROC(false, dev, BindImageMemory2);
-    INIT_PROC(false, dev, GetDeviceGroupPeerMemoryFeatures);
-    INIT_PROC(false, dev, CmdSetDeviceMask);
-    INIT_PROC(false, dev, CmdDispatchBase);
-    INIT_PROC(false, dev, GetImageMemoryRequirements2);
-    INIT_PROC(false, dev, GetBufferMemoryRequirements2);
-    INIT_PROC(false, dev, GetImageSparseMemoryRequirements2);
-    INIT_PROC(false, dev, TrimCommandPool);
-    INIT_PROC(false, dev, GetDeviceQueue2);
-    INIT_PROC(false, dev, CreateSamplerYcbcrConversion);
-    INIT_PROC(false, dev, DestroySamplerYcbcrConversion);
-    INIT_PROC(false, dev, CreateDescriptorUpdateTemplate);
-    INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate);
-    INIT_PROC(false, dev, UpdateDescriptorSetWithTemplate);
-    INIT_PROC(false, dev, GetDescriptorSetLayoutSupport);
     INIT_PROC_EXT(KHR_swapchain, true, dev, CreateSwapchainKHR);
     INIT_PROC_EXT(KHR_swapchain, true, dev, DestroySwapchainKHR);
     INIT_PROC_EXT(KHR_swapchain, true, dev, GetSwapchainImagesKHR);
     INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImageKHR);
     INIT_PROC_EXT(KHR_swapchain, true, dev, QueuePresentKHR);
+    INIT_PROC(false, dev, TrimCommandPool);
+    INIT_PROC(false, dev, GetDeviceGroupPeerMemoryFeatures);
+    INIT_PROC(false, dev, BindBufferMemory2);
+    INIT_PROC(false, dev, BindImageMemory2);
+    INIT_PROC(false, dev, CmdSetDeviceMask);
     INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupPresentCapabilitiesKHR);
     INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupSurfacePresentModesKHR);
     INIT_PROC_EXT(KHR_swapchain, false, dev, AcquireNextImage2KHR);
-    INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, false, dev, GetAndroidHardwareBufferPropertiesANDROID);
-    INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, false, dev, GetMemoryAndroidHardwareBufferANDROID);
+    INIT_PROC(false, dev, CmdDispatchBase);
+    INIT_PROC(false, dev, CreateDescriptorUpdateTemplate);
+    INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate);
+    INIT_PROC(false, dev, UpdateDescriptorSetWithTemplate);
+    INIT_PROC(false, dev, GetBufferMemoryRequirements2);
+    INIT_PROC(false, dev, GetImageMemoryRequirements2);
+    INIT_PROC(false, dev, GetImageSparseMemoryRequirements2);
+    INIT_PROC(false, dev, CreateSamplerYcbcrConversion);
+    INIT_PROC(false, dev, DestroySamplerYcbcrConversion);
+    INIT_PROC(false, dev, GetDeviceQueue2);
+    INIT_PROC(false, dev, GetDescriptorSetLayoutSupport);
+    INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetAndroidHardwareBufferPropertiesANDROID);
+    INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetMemoryAndroidHardwareBufferANDROID);
     // clang-format on
 
     return success;
@@ -479,33 +478,7 @@
 VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents);
 VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer);
 VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
-VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos);
-VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
-VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures);
-VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask);
-VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
-VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
-VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
-VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
-VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
-VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures);
-VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties);
-VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties);
-VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties);
-VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
-VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
-VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
-VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
-VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
-VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion);
-VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
-VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
-VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
-VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties);
-VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
-VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
+VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
 VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported);
 VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities);
@@ -516,11 +489,37 @@
 VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages);
 VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex);
 VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
+VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties);
+VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
+VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures);
+VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos);
+VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
+VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask);
 VKAPI_ATTR VkResult GetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities);
 VKAPI_ATTR VkResult GetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR* pModes);
-VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects);
 VKAPI_ATTR VkResult AcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex);
-VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface);
+VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects);
+VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
+VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
+VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
+VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion);
+VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
+VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
 VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties);
 VKAPI_ATTR VkResult GetMemoryAndroidHardwareBufferANDROID(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer);
 
@@ -622,9 +621,9 @@
     // global functions
     if (instance == VK_NULL_HANDLE) {
         if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance);
+        if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion);
         if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceLayerProperties);
         if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties);
-        if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion);
 
         ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \"%s\") call", pName);
         return nullptr;
@@ -1313,112 +1312,8 @@
     GetData(commandBuffer).dispatch.CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers);
 }
 
-VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) {
-    return GetData(device).dispatch.BindBufferMemory2(device, bindInfoCount, pBindInfos);
-}
-
-VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
-    return GetData(device).dispatch.BindImageMemory2(device, bindInfoCount, pBindInfos);
-}
-
-VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) {
-    GetData(device).dispatch.GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures);
-}
-
-VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) {
-    GetData(commandBuffer).dispatch.CmdSetDeviceMask(commandBuffer, deviceMask);
-}
-
-VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
-    GetData(commandBuffer).dispatch.CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ);
-}
-
-VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
-    return GetData(instance).dispatch.EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
-}
-
-VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
-    GetData(device).dispatch.GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements);
-}
-
-VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
-    GetData(device).dispatch.GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
-}
-
-VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
-    GetData(device).dispatch.GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceProperties2(physicalDevice, pProperties);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceFormatProperties2(physicalDevice, format, pFormatProperties);
-}
-
-VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties) {
-    return GetData(physicalDevice).dispatch.GetPhysicalDeviceImageFormatProperties2(physicalDevice, pImageFormatInfo, pImageFormatProperties);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceQueueFamilyProperties2(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceMemoryProperties2(physicalDevice, pMemoryProperties);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceSparseImageFormatProperties2(physicalDevice, pFormatInfo, pPropertyCount, pProperties);
-}
-
-VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
-    GetData(device).dispatch.TrimCommandPool(device, commandPool, flags);
-}
-
-VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) {
-    GetData(device).dispatch.GetDeviceQueue2(device, pQueueInfo, pQueue);
-}
-
-VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) {
-    return GetData(device).dispatch.CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion);
-}
-
-VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) {
-    GetData(device).dispatch.DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator);
-}
-
-VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) {
-    return GetData(device).dispatch.CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate);
-}
-
-VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) {
-    GetData(device).dispatch.DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator);
-}
-
-VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) {
-    GetData(device).dispatch.UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
-}
-
-VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
-    GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties);
-}
-
-VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
-    GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
+VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
+    return GetData(instance).dispatch.CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
 }
 
 VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator) {
@@ -1461,6 +1356,70 @@
     return GetData(queue).dispatch.QueuePresentKHR(queue, pPresentInfo);
 }
 
+VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceProperties2(physicalDevice, pProperties);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceFormatProperties2(physicalDevice, format, pFormatProperties);
+}
+
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties) {
+    return GetData(physicalDevice).dispatch.GetPhysicalDeviceImageFormatProperties2(physicalDevice, pImageFormatInfo, pImageFormatProperties);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceQueueFamilyProperties2(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceMemoryProperties2(physicalDevice, pMemoryProperties);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceSparseImageFormatProperties2(physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+}
+
+VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
+    GetData(device).dispatch.TrimCommandPool(device, commandPool, flags);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties);
+}
+
+VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) {
+    GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+}
+
+VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+    return GetData(instance).dispatch.EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
+}
+
+VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) {
+    GetData(device).dispatch.GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures);
+}
+
+VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) {
+    return GetData(device).dispatch.BindBufferMemory2(device, bindInfoCount, pBindInfos);
+}
+
+VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
+    return GetData(device).dispatch.BindImageMemory2(device, bindInfoCount, pBindInfos);
+}
+
+VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) {
+    GetData(commandBuffer).dispatch.CmdSetDeviceMask(commandBuffer, deviceMask);
+}
+
 VKAPI_ATTR VkResult GetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
     return GetData(device).dispatch.GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities);
 }
@@ -1469,16 +1428,56 @@
     return GetData(device).dispatch.GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes);
 }
 
-VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) {
-    return GetData(physicalDevice).dispatch.GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects);
-}
-
 VKAPI_ATTR VkResult AcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex) {
     return GetData(device).dispatch.AcquireNextImage2KHR(device, pAcquireInfo, pImageIndex);
 }
 
-VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
-    return GetData(instance).dispatch.CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
+    GetData(commandBuffer).dispatch.CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ);
+}
+
+VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) {
+    return GetData(physicalDevice).dispatch.GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects);
+}
+
+VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) {
+    return GetData(device).dispatch.CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate);
+}
+
+VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) {
+    GetData(device).dispatch.DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator);
+}
+
+VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) {
+    GetData(device).dispatch.UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData);
+}
+
+VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    GetData(device).dispatch.GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
+}
+
+VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    GetData(device).dispatch.GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements);
+}
+
+VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
+    GetData(device).dispatch.GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
+}
+
+VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) {
+    return GetData(device).dispatch.CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion);
+}
+
+VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) {
+    GetData(device).dispatch.DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator);
+}
+
+VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) {
+    GetData(device).dispatch.GetDeviceQueue2(device, pQueueInfo, pQueue);
+}
+
+VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
+    GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
 }
 
 VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) {
@@ -1565,6 +1564,11 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEnumerateInstanceVersion(uint32_t* pApiVersion) {
+    return vulkan::api::EnumerateInstanceVersion(pApiVersion);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties) {
     return vulkan::api::EnumerateInstanceLayerProperties(pPropertyCount, pProperties);
 }
@@ -2185,143 +2189,8 @@
 }
 
 __attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEnumerateInstanceVersion(uint32_t* pApiVersion) {
-    return vulkan::api::EnumerateInstanceVersion(pApiVersion);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkBindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) {
-    return vulkan::api::BindBufferMemory2(device, bindInfoCount, pBindInfos);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
-    return vulkan::api::BindImageMemory2(device, bindInfoCount, pBindInfos);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) {
-    vulkan::api::GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) {
-    vulkan::api::CmdSetDeviceMask(commandBuffer, deviceMask);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkCmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
-    vulkan::api::CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
-    return vulkan::api::EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
-    vulkan::api::GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
-    vulkan::api::GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
-    vulkan::api::GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
-    vulkan::api::GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties) {
-    vulkan::api::GetPhysicalDeviceProperties2(physicalDevice, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties) {
-    vulkan::api::GetPhysicalDeviceFormatProperties2(physicalDevice, format, pFormatProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties) {
-    return vulkan::api::GetPhysicalDeviceImageFormatProperties2(physicalDevice, pImageFormatInfo, pImageFormatProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties) {
-    vulkan::api::GetPhysicalDeviceQueueFamilyProperties2(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
-    vulkan::api::GetPhysicalDeviceMemoryProperties2(physicalDevice, pMemoryProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {
-    vulkan::api::GetPhysicalDeviceSparseImageFormatProperties2(physicalDevice, pFormatInfo, pPropertyCount, pProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkTrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
-    vulkan::api::TrimCommandPool(device, commandPool, flags);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) {
-    vulkan::api::GetDeviceQueue2(device, pQueueInfo, pQueue);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) {
-    return vulkan::api::CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) {
-    vulkan::api::DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) {
-    return vulkan::api::CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkDestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) {
-    vulkan::api::DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkUpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) {
-    vulkan::api::UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) {
-    vulkan::api::GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) {
-    vulkan::api::GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
-    vulkan::api::GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties);
-}
-
-__attribute__((visibility("default")))
-VKAPI_ATTR void vkGetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
-    vulkan::api::GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
+VKAPI_ATTR VkResult vkCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
+    return vulkan::api::CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
 }
 
 __attribute__((visibility("default")))
@@ -2375,6 +2244,86 @@
 }
 
 __attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) {
+    vulkan::api::GetPhysicalDeviceFeatures2(physicalDevice, pFeatures);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties) {
+    vulkan::api::GetPhysicalDeviceProperties2(physicalDevice, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties) {
+    vulkan::api::GetPhysicalDeviceFormatProperties2(physicalDevice, format, pFormatProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties) {
+    return vulkan::api::GetPhysicalDeviceImageFormatProperties2(physicalDevice, pImageFormatInfo, pImageFormatProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties) {
+    vulkan::api::GetPhysicalDeviceQueueFamilyProperties2(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
+    vulkan::api::GetPhysicalDeviceMemoryProperties2(physicalDevice, pMemoryProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties) {
+    vulkan::api::GetPhysicalDeviceSparseImageFormatProperties2(physicalDevice, pFormatInfo, pPropertyCount, pProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkTrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags) {
+    vulkan::api::TrimCommandPool(device, commandPool, flags);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) {
+    vulkan::api::GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) {
+    vulkan::api::GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) {
+    vulkan::api::GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) {
+    return vulkan::api::EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) {
+    vulkan::api::GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkBindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) {
+    return vulkan::api::BindBufferMemory2(device, bindInfoCount, pBindInfos);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
+    return vulkan::api::BindImageMemory2(device, bindInfoCount, pBindInfos);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkCmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) {
+    vulkan::api::CmdSetDeviceMask(commandBuffer, deviceMask);
+}
+
+__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkGetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
     return vulkan::api::GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities);
 }
@@ -2385,18 +2334,68 @@
 }
 
 __attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) {
-    return vulkan::api::GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects);
-}
-
-__attribute__((visibility("default")))
 VKAPI_ATTR VkResult vkAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex) {
     return vulkan::api::AcquireNextImage2KHR(device, pAcquireInfo, pImageIndex);
 }
 
 __attribute__((visibility("default")))
-VKAPI_ATTR VkResult vkCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) {
-    return vulkan::api::CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface);
+VKAPI_ATTR void vkCmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
+    vulkan::api::CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) {
+    return vulkan::api::GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) {
+    return vulkan::api::CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkUpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) {
+    vulkan::api::UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    vulkan::api::GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
+    vulkan::api::GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) {
+    vulkan::api::GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR VkResult vkCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) {
+    return vulkan::api::CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkDestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) {
+    vulkan::api::DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) {
+    vulkan::api::GetDeviceQueue2(device, pQueueInfo, pQueue);
+}
+
+__attribute__((visibility("default")))
+VKAPI_ATTR void vkGetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) {
+    vulkan::api::GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport);
 }
 
 __attribute__((visibility("default")))
diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h
index 4bedbeb..2195845 100644
--- a/vulkan/libvulkan/api_gen.h
+++ b/vulkan/libvulkan/api_gen.h
@@ -20,7 +20,9 @@
 #define LIBVULKAN_API_GEN_H
 
 #include <vulkan/vulkan.h>
+
 #include <bitset>
+
 #include "driver_gen.h"
 
 namespace vulkan {
@@ -40,7 +42,12 @@
     PFN_vkCreateDevice CreateDevice;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
-    PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
+    PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR;
+    PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
+    PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
+    PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR;
+    PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR;
+    PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR;
     PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2;
     PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2;
     PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2;
@@ -49,15 +56,10 @@
     PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 GetPhysicalDeviceSparseImageFormatProperties2;
     PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties;
-    PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
     PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties;
-    PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
-    PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
-    PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR;
-    PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR;
-    PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR;
+    PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties;
+    PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
     PFN_vkGetPhysicalDevicePresentRectanglesKHR GetPhysicalDevicePresentRectanglesKHR;
-    PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR;
     // clang-format on
 };
 
@@ -184,30 +186,30 @@
     PFN_vkCmdNextSubpass CmdNextSubpass;
     PFN_vkCmdEndRenderPass CmdEndRenderPass;
     PFN_vkCmdExecuteCommands CmdExecuteCommands;
-    PFN_vkBindBufferMemory2 BindBufferMemory2;
-    PFN_vkBindImageMemory2 BindImageMemory2;
-    PFN_vkGetDeviceGroupPeerMemoryFeatures GetDeviceGroupPeerMemoryFeatures;
-    PFN_vkCmdSetDeviceMask CmdSetDeviceMask;
-    PFN_vkCmdDispatchBase CmdDispatchBase;
-    PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2;
-    PFN_vkGetBufferMemoryRequirements2 GetBufferMemoryRequirements2;
-    PFN_vkGetImageSparseMemoryRequirements2 GetImageSparseMemoryRequirements2;
-    PFN_vkTrimCommandPool TrimCommandPool;
-    PFN_vkGetDeviceQueue2 GetDeviceQueue2;
-    PFN_vkCreateSamplerYcbcrConversion CreateSamplerYcbcrConversion;
-    PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion;
-    PFN_vkCreateDescriptorUpdateTemplate CreateDescriptorUpdateTemplate;
-    PFN_vkDestroyDescriptorUpdateTemplate DestroyDescriptorUpdateTemplate;
-    PFN_vkUpdateDescriptorSetWithTemplate UpdateDescriptorSetWithTemplate;
-    PFN_vkGetDescriptorSetLayoutSupport GetDescriptorSetLayoutSupport;
     PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
     PFN_vkDestroySwapchainKHR DestroySwapchainKHR;
     PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
     PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
     PFN_vkQueuePresentKHR QueuePresentKHR;
+    PFN_vkTrimCommandPool TrimCommandPool;
+    PFN_vkGetDeviceGroupPeerMemoryFeatures GetDeviceGroupPeerMemoryFeatures;
+    PFN_vkBindBufferMemory2 BindBufferMemory2;
+    PFN_vkBindImageMemory2 BindImageMemory2;
+    PFN_vkCmdSetDeviceMask CmdSetDeviceMask;
     PFN_vkGetDeviceGroupPresentCapabilitiesKHR GetDeviceGroupPresentCapabilitiesKHR;
     PFN_vkGetDeviceGroupSurfacePresentModesKHR GetDeviceGroupSurfacePresentModesKHR;
     PFN_vkAcquireNextImage2KHR AcquireNextImage2KHR;
+    PFN_vkCmdDispatchBase CmdDispatchBase;
+    PFN_vkCreateDescriptorUpdateTemplate CreateDescriptorUpdateTemplate;
+    PFN_vkDestroyDescriptorUpdateTemplate DestroyDescriptorUpdateTemplate;
+    PFN_vkUpdateDescriptorSetWithTemplate UpdateDescriptorSetWithTemplate;
+    PFN_vkGetBufferMemoryRequirements2 GetBufferMemoryRequirements2;
+    PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2;
+    PFN_vkGetImageSparseMemoryRequirements2 GetImageSparseMemoryRequirements2;
+    PFN_vkCreateSamplerYcbcrConversion CreateSamplerYcbcrConversion;
+    PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion;
+    PFN_vkGetDeviceQueue2 GetDeviceQueue2;
+    PFN_vkGetDescriptorSetLayoutSupport GetDescriptorSetLayoutSupport;
     PFN_vkGetAndroidHardwareBufferPropertiesANDROID GetAndroidHardwareBufferPropertiesANDROID;
     PFN_vkGetMemoryAndroidHardwareBufferANDROID GetMemoryAndroidHardwareBufferANDROID;
     // clang-format on
diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl
deleted file mode 100644
index a5a0405..0000000
--- a/vulkan/libvulkan/code-generator.tmpl
+++ /dev/null
@@ -1,1224 +0,0 @@
-{{define "Copyright"}}
-/*
-•* 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.
-•*/
-¶{{end}}
-
-{{Include "../api/templates/vulkan_common.tmpl"}}
-{{Global "clang-format" (Strings "clang-format" "-style=file")}}
-{{Macro "DefineGlobals" $}}
-{{$ | Macro "api_gen.h"   | Format (Global "clang-format") | Write "api_gen.h"  }}
-{{$ | Macro "api_gen.cpp" | Format (Global "clang-format") | Write "api_gen.cpp"}}
-{{$ | Macro "driver_gen.h" | Format (Global "clang-format") | Write "driver_gen.h"}}
-{{$ | Macro "driver_gen.cpp" | Format (Global "clang-format") | Write "driver_gen.cpp"}}
-
-{{/*
--------------------------------------------------------------------------------
-  api_gen.h
--------------------------------------------------------------------------------
-*/}}
-{{define "api_gen.h"}}
-{{Macro "Copyright"}}

-// WARNING: This file is generated. See ../README.md for instructions.

-#ifndef LIBVULKAN_API_GEN_H
-#define LIBVULKAN_API_GEN_H

-#include <bitset>
-#include <vulkan/vulkan.h>
-#include "driver_gen.h"

-namespace vulkan {«
-namespace api {«

-struct InstanceDispatchTable {
-  // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}}
-      {{Macro "C++.DeclareTableEntry" $f}};
-    {{end}}
-  {{end}}
-  // clang-format on
-};

-struct DeviceDispatchTable {
-  // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}}
-      {{Macro "C++.DeclareTableEntry" $f}};
-    {{end}}
-  {{end}}
-  // clang-format on
-};

-bool InitDispatchTable(
-    VkInstance instance,
-    PFN_vkGetInstanceProcAddr get_proc,
-    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions);
-bool InitDispatchTable(
-    VkDevice dev,
-    PFN_vkGetDeviceProcAddr get_proc,
-    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions);

-»} // namespace api
-»} // namespace vulkan

-#endif // LIBVULKAN_API_GEN_H
-¶{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  api_gen.cpp
--------------------------------------------------------------------------------
-*/}}
-{{define "api_gen.cpp"}}
-{{Macro "Copyright"}}

-// WARNING: This file is generated. See ../README.md for instructions.

-#include <string.h>

-#include <algorithm>

-#include <log/log.h>

-// to catch mismatches between vulkan.h and this file
-#undef VK_NO_PROTOTYPES
-#include "api.h"

-namespace vulkan {«
-namespace api {«

-{{Macro "C++.DefineInitProcMacro" "dispatch"}}

-{{Macro "api.C++.DefineInitProcExtMacro"}}

-namespace {«

-// clang-format off

-{{range $f := AllCommands $}}
-  {{Macro "api.C++.DefineExtensionStub" $f}}
-{{end}}
-// clang-format on

-»} // anonymous

-bool InitDispatchTable(
-    VkInstance instance,
-    PFN_vkGetInstanceProcAddr get_proc,
-    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) {
-    auto& data = GetData(instance);
-    bool success = true;
-    ¶
-    // clang-format off
-    {{range $f := AllCommands $}}
-      {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}}
-        {{Macro "C++.InitProc" $f}}
-      {{end}}
-    {{end}}
-    // clang-format on
-    ¶
-    return success;
-}

-bool InitDispatchTable(
-    VkDevice dev,
-    PFN_vkGetDeviceProcAddr get_proc,
-    const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) {
-    auto& data = GetData(dev);
-    bool success = true;
-    ¶
-    // clang-format off
-    {{range $f := AllCommands $}}
-      {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}}
-        {{Macro "C++.InitProc" $f}}
-      {{end}}
-    {{end}}
-    // clang-format on
-    ¶
-    return success;
-}

-// clang-format off

-namespace {«

-// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr
-{{range $f := AllCommands $}}
-  {{if and (Macro "IsFunctionExported" $f) (not (Macro "api.IsIntercepted" $f))}}
-    VKAPI_ATTR {{Node "Type" $f.Return}} {{Macro "BaseName" $f}}({{Macro "Parameters" $f}});
-  {{end}}
-{{end}}

-{{range $f := AllCommands $}}
-  {{if and (Macro "IsFunctionExported" $f) (not (Macro "api.IsIntercepted" $f))}}
-    VKAPI_ATTR {{Node "Type" $f.Return}} {{Macro "BaseName" $f}}({{Macro "Parameters" $f}}) {
-      {{     if eq $f.Name "vkGetInstanceProcAddr"}}
-        {{Macro "api.C++.InterceptInstanceProcAddr" $}}
-      {{else if eq $f.Name "vkGetDeviceProcAddr"}}
-        {{Macro "api.C++.InterceptDeviceProcAddr" $}}
-      {{end}}
-
-      {{Macro "api.C++.Dispatch" $f}}
-    }
-    ¶
-  {{end}}
-{{end}}

-»}  // anonymous namespace

-// clang-format on

-»} // namespace api
-»} // namespace vulkan

-// clang-format off

-{{range $f := AllCommands $}}
-  {{if (Macro "IsFunctionExported" $f)}}
-    __attribute__((visibility("default")))
-    VKAPI_ATTR {{Node "Type" $f.Return}} {{$f.Name}}({{Macro "Parameters" $f}}) {
-      {{if not (IsVoid $f.Return.Type)}}return §{{end}}
-      vulkan::api::{{Macro "BaseName" $f}}({{Macro "Arguments" $f}});
-    }
-    ¶
-  {{end}}
-{{end}}

-// clang-format on
-¶{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  driver_gen.h
--------------------------------------------------------------------------------
-*/}}
-{{define "driver_gen.h"}}
-{{Macro "Copyright"}}

-// WARNING: This file is generated. See ../README.md for instructions.

-#ifndef LIBVULKAN_DRIVER_GEN_H
-#define LIBVULKAN_DRIVER_GEN_H

-#include <bitset>
-#include <vulkan/vulkan.h>
-#include <vulkan/vk_android_native_buffer.h>

-namespace vulkan {«
-namespace driver {«

-{{Macro "driver.C++.DefineProcHookType"}}

-struct InstanceDriverTable {
-  // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "driver.IsInstanceDriverTableEntry" $f)}}
-      {{Macro "C++.DeclareTableEntry" $f}};
-    {{end}}
-  {{end}}
-  // clang-format on
-};

-struct DeviceDriverTable {
-  // clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "driver.IsDeviceDriverTableEntry" $f)}}
-      {{Macro "C++.DeclareTableEntry" $f}};
-    {{end}}
-  {{end}}
-  // clang-format on
-};

-const ProcHook* GetProcHook(const char* name);
-ProcHook::Extension GetProcHookExtension(const char* name);

-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
-                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions);
-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
-                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions);

-»} // namespace driver
-»} // namespace vulkan

-#endif // LIBVULKAN_DRIVER_TABLE_H
-¶{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  driver_gen.cpp
--------------------------------------------------------------------------------
-*/}}
-{{define "driver_gen.cpp"}}
-{{Macro "Copyright"}}

-// WARNING: This file is generated. See ../README.md for instructions.

-#include <string.h>

-#include <algorithm>

-#include <log/log.h>

-#include "driver.h"

-namespace vulkan {«
-namespace driver {«

-namespace {«

-// clang-format off

-{{range $f := AllCommands $}}
-  {{Macro "driver.C++.DefineProcHookStub" $f}}
-{{end}}
-// clang-format on

-const ProcHook g_proc_hooks[] = {
-  // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if (Macro "driver.IsIntercepted" $f)}}
-      {{     if (Macro "IsGloballyDispatched" $f)}}
-        {{Macro "driver.C++.DefineGlobalProcHook" $f}}
-      {{else if (Macro "IsInstanceDispatched" $f)}}
-        {{Macro "driver.C++.DefineInstanceProcHook" $f}}
-      {{else if (Macro "IsDeviceDispatched" $f)}}
-        {{Macro "driver.C++.DefineDeviceProcHook" $f}}
-      {{end}}
-    {{end}}
-  {{end}}
-  // clang-format on
-};

-»} // anonymous

-const ProcHook* GetProcHook(const char* name) {
-    const auto& begin = g_proc_hooks;
-    const auto& end = g_proc_hooks +
-      sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
-    const auto hook = std::lower_bound(begin, end, name,
-        [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
-    return (hook <  end && strcmp(hook->name, name) == 0) ? hook : nullptr;
-}

-ProcHook::Extension GetProcHookExtension(const char* name) {
-  {{$exts := Strings (Macro "driver.KnownExtensions") | SplitOn "\n"}}
-  // clang-format off
-  {{range $e := $exts}}
-    if (strcmp(name, "{{$e}}") == 0) return ProcHook::{{TrimPrefix "VK_" $e}};
-  {{end}}
-  // clang-format on
-  return ProcHook::EXTENSION_UNKNOWN;
-}

-{{Macro "C++.DefineInitProcMacro" "driver"}}

-{{Macro "driver.C++.DefineInitProcExtMacro"}}

-bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc,
-                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
-{
-    auto& data = GetData(instance);
-    bool success = true;
-    ¶
-    // clang-format off
-    {{range $f := AllCommands $}}
-      {{if (Macro "driver.IsInstanceDriverTableEntry" $f)}}
-        {{Macro "C++.InitProc" $f}}
-      {{end}}
-    {{end}}
-    // clang-format on
-    ¶
-    return success;
-}

-bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc,
-                     const std::bitset<ProcHook::EXTENSION_COUNT> &extensions)
-{
-    auto& data = GetData(dev);
-    bool success = true;
-    ¶
-    // clang-format off
-    {{range $f := AllCommands $}}
-      {{if (Macro "driver.IsDeviceDriverTableEntry" $f)}}
-        {{Macro "C++.InitProc" $f}}
-      {{end}}
-    {{end}}
-    // clang-format on
-    ¶
-    return success;
-}

-»} // namespace driver
-»} // namespace vulkan

-// clang-format on
-¶{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits a declaration of a dispatch/driver table entry.
-------------------------------------------------------------------------------
-*/}}
-{{define "C++.DeclareTableEntry"}}
-  {{AssertType $ "Function"}}
-
-  {{Macro "FunctionPtrName" $}} {{Macro "BaseName" $}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits INIT_PROC macro.
--------------------------------------------------------------------------------
-*/}}
-{{define "C++.DefineInitProcMacro"}}
-  #define UNLIKELY(expr) __builtin_expect((expr), 0)
-  ¶
-  #define INIT_PROC(required, obj, proc) do {                   \
-      data.{{$}}.proc = reinterpret_cast<PFN_vk ## proc>(       \
-              get_proc(obj, "vk" # proc));                      \
-      if (UNLIKELY(required && !data.{{$}}.proc)) {             \
-          ALOGE("missing " # obj " proc: vk" # proc);           \
-          success = false;                                      \
-      }                                                         \
-  } while(0)
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits code to invoke INIT_PROC or INIT_PROC_EXT.
--------------------------------------------------------------------------------
-*/}}
-{{define "C++.InitProc"}}
-  {{AssertType $ "Function"}}
-
-  {{$ext := GetAnnotation $ "extension"}}
-  {{if $ext}}
-    INIT_PROC_EXT({{Macro "BaseName" $ext}}, §
-  {{else}}
-    INIT_PROC(§
-  {{end}}
-
-  {{if GetAnnotation $ "optional"}}false{{else if GetAnnotation $ "vulkan1_1"}}false{{else}}true{{end}}, §
-
-  {{if (Macro "IsInstanceDispatched" $)}}
-    instance, §
-  {{else}}
-    dev, §
-  {{end}}
-
-  {{Macro "BaseName" $}});
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if a function is exported and instance-dispatched.
-------------------------------------------------------------------------------
-*/}}
-{{define "api.IsInstanceDispatchTableEntry"}}
-  {{AssertType $ "Function"}}
-
-  {{if and (Macro "IsFunctionExported" $) (Macro "IsInstanceDispatched" $)}}
-    {{/* deprecated and unused internally */}}
-    {{if not (eq $.Name "vkEnumerateDeviceLayerProperties")}}
-      true
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if a function is exported and device-dispatched.
-------------------------------------------------------------------------------
-*/}}
-{{define "api.IsDeviceDispatchTableEntry"}}
-  {{AssertType $ "Function"}}
-
-  {{if and (Macro "IsFunctionExported" $) (Macro "IsDeviceDispatched" $)}}
-    true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if a function is intercepted by vulkan::api.
-------------------------------------------------------------------------------
-*/}}
-{{define "api.IsIntercepted"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "IsFunctionSupported" $)}}
-    {{/* Global functions cannot be dispatched at all */}}
-    {{     if (Macro "IsGloballyDispatched" $)}}true
-
-    {{/* VkPhysicalDevice functions that manage device layers */}}
-    {{else if eq $.Name "vkCreateDevice"}}true
-    {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true
-    {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
-
-    {{/* Destroy functions of dispatchable objects */}}
-    {{else if eq $.Name "vkDestroyInstance"}}true
-    {{else if eq $.Name "vkDestroyDevice"}}true
-
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits INIT_PROC_EXT macro for vulkan::api.
--------------------------------------------------------------------------------
-*/}}
-{{define "api.C++.DefineInitProcExtMacro"}}
-  // Exported extension functions may be invoked even when their extensions
-  // are disabled.  Dispatch to stubs when that happens.
-  #define INIT_PROC_EXT(ext, required, obj, proc) do {          \
-      if (extensions[driver::ProcHook::ext])                    \
-        INIT_PROC(required, obj, proc);                         \
-      else                                                      \
-        data.dispatch.proc = disabled ## proc;                  \
-  } while(0)
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a stub for an exported extension function.
--------------------------------------------------------------------------------
-*/}}
-{{define "api.C++.DefineExtensionStub"}}
-  {{AssertType $ "Function"}}
-
-  {{$ext := GetAnnotation $ "extension"}}
-  {{if and $ext (Macro "IsFunctionExported" $)}}
-    {{$ext_name := index $ext.Arguments 0}}
-
-    {{$base := (Macro "BaseName" $)}}
-
-    {{$p0 := (index $.CallParameters 0)}}
-    {{$ptail := (Tail 1 $.CallParameters)}}
-
-    {{$first_type := (Macro "Parameter" $p0)}}
-    {{$tail_types := (ForEach $ptail "ParameterType" | JoinWith ", ")}}
-
-    VKAPI_ATTR {{Node "Type" $.Return}} disabled{{$base}}({{$first_type}}, {{$tail_types}}) {
-      driver::Logger({{$p0.Name}}).Err({{$p0.Name}}, §
-        "{{$ext_name}} not enabled. Exported {{$.Name}} not executed.");
-      {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
-    }
-    ¶
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits code for vkGetInstanceProcAddr for function interception.
-------------------------------------------------------------------------------
-*/}}
-{{define "api.C++.InterceptInstanceProcAddr"}}
-  {{AssertType $ "API"}}
-
-  // global functions
-  if (instance == VK_NULL_HANDLE) {
-    {{range $f := AllCommands $}}
-      {{if (Macro "IsGloballyDispatched" $f)}}
-        if (strcmp(pName, "{{$f.Name}}") == 0) return §
-          reinterpret_cast<PFN_vkVoidFunction>({{Macro "BaseName" $f}});
-      {{end}}
-    {{end}}
-    ¶
-    ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \"%s\") call", pName);
-    return nullptr;
-  }
-  ¶
-  static const struct Hook {
-    const char* name;
-    PFN_vkVoidFunction proc;
-  } hooks[] = {
-    {{range $f := SortBy (AllCommands $) "FunctionName"}}
-      {{if (Macro "IsFunctionExported" $f)}}
-        {{/* hide global functions */}}
-        {{if (Macro "IsGloballyDispatched" $f)}}
-          { "{{$f.Name}}", nullptr },
-
-        {{/* redirect intercepted functions */}}
-        {{else if (Macro "api.IsIntercepted" $f)}}
-          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
-            {{Macro "BaseName" $f}}) },
-
-        {{/* redirect vkGetInstanceProcAddr to itself */}}
-        {{else if eq $f.Name "vkGetInstanceProcAddr"}}
-          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{Macro "BaseName" $f}}) },
-
-        {{/* redirect device functions to themselves as a workaround for
-             layers that do not intercept in their vkGetInstanceProcAddr */}}
-        {{else if (Macro "IsDeviceDispatched" $f)}}
-          { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{Macro "BaseName" $f}}) },
-
-        {{end}}
-      {{end}}
-    {{end}}
-  };
-  // clang-format on
-  constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
-  auto hook = std::lower_bound(
-    hooks, hooks + count, pName,
-    [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
-  if (hook <  hooks + count && strcmp(hook->name, pName) == 0) {
-    if (!hook->proc) {
-      vulkan::driver::Logger(instance).Err(
-        instance, "invalid vkGetInstanceProcAddr(%p, \"%s\") call",
-        instance, pName);
-    }
-    return hook->proc;
-  }
-  // clang-format off
-  ¶
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits code for vkGetDeviceProcAddr for function interception.
-------------------------------------------------------------------------------
-*/}}
-{{define "api.C++.InterceptDeviceProcAddr"}}
-  {{AssertType $ "API"}}
-
-  if (device == VK_NULL_HANDLE) {
-    ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call");
-    return nullptr;
-  }
-  ¶
-  static const char* const known_non_device_names[] = {
-    {{range $f := SortBy (AllCommands $) "FunctionName"}}
-      {{if (Macro "IsFunctionSupported" $f)}}
-        {{if not (Macro "IsDeviceDispatched" $f)}}
-          "{{$f.Name}}",
-        {{end}}
-      {{end}}
-    {{end}}
-  };
-  // clang-format on
-  constexpr size_t count = sizeof(known_non_device_names) /
-    sizeof(known_non_device_names[0]);
-  if (!pName ||
-      std::binary_search(
-        known_non_device_names, known_non_device_names + count, pName,
-        [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
-    vulkan::driver::Logger(device).Err(§
-      device, "invalid vkGetDeviceProcAddr(%p, \"%s\") call", device,§
-      (pName) ? pName : "(null)");
-    return nullptr;
-  }
-  // clang-format off
-  ¶
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsDeviceDispatched" $f)}}
-      {{     if (Macro "api.IsIntercepted" $f)}}
-        if (strcmp(pName, "{{$f.Name}}") == 0) return §
-          reinterpret_cast<PFN_vkVoidFunction>(§
-            {{Macro "BaseName" $f}});
-      {{else if eq $f.Name "vkGetDeviceProcAddr"}}
-        if (strcmp(pName, "{{$f.Name}}") == 0) return §
-          reinterpret_cast<PFN_vkVoidFunction>(§
-            {{Macro "BaseName" $f}});
-      {{end}}
-    {{end}}
-  {{end}}
-  ¶
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits code to dispatch a function.
-------------------------------------------------------------------------------
-*/}}
-{{define "api.C++.Dispatch"}}
-  {{AssertType $ "Function"}}
-  {{if (Macro "api.IsIntercepted" $)}}
-    {{Error "$.Name should not be generated"}}
-  {{end}}
-
-  {{if not (IsVoid $.Return.Type)}}return §{{end}}
-
-  {{$p0 := index $.CallParameters 0}}
-  GetData({{$p0.Name}}).dispatch.§
-  {{Macro "BaseName" $}}({{Macro "Arguments" $}});
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits a list of extensions intercepted by vulkan::driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.InterceptedExtensions"}}
-VK_ANDROID_native_buffer
-VK_EXT_debug_report
-VK_EXT_hdr_metadata
-VK_EXT_swapchain_colorspace
-VK_GOOGLE_display_timing
-VK_KHR_android_surface
-VK_KHR_incremental_present
-VK_KHR_shared_presentable_image
-VK_KHR_surface
-VK_KHR_swapchain
-VK_KHR_get_surface_capabilities2
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits a list of extensions known to vulkan::driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.KnownExtensions"}}
-{{Macro "driver.InterceptedExtensions"}}
-VK_KHR_get_physical_device_properties2
-VK_ANDROID_external_memory_android_hardware_buffer
-VK_KHR_bind_memory2
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if an extension is intercepted by vulkan::driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.IsExtensionIntercepted"}}
-  {{$ext_name := index $.Arguments 0}}
-  {{$filters := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}}
-
-  {{range $f := $filters}}
-    {{if eq $ext_name $f}}true{{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if a function is intercepted by vulkan::driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.IsIntercepted"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "IsFunctionSupported" $)}}
-    {{/* Create functions of dispatchable objects */}}
-    {{     if eq $.Name "vkCreateInstance"}}true
-    {{else if eq $.Name "vkCreateDevice"}}true
-    {{else if eq $.Name "vkEnumeratePhysicalDevices"}}true
-    {{else if eq $.Name "vkEnumeratePhysicalDeviceGroups"}}true
-    {{else if eq $.Name "vkGetDeviceQueue"}}true
-    {{else if eq $.Name "vkGetDeviceQueue2"}}true
-    {{else if eq $.Name "vkAllocateCommandBuffers"}}true
-
-    {{/* Destroy functions of dispatchable objects */}}
-    {{else if eq $.Name "vkDestroyInstance"}}true
-    {{else if eq $.Name "vkDestroyDevice"}}true
-
-    {{/* Enumeration of extensions */}}
-    {{else if eq $.Name "vkEnumerateInstanceExtensionProperties"}}true
-    {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
-
-    {{else if eq $.Name "vkGetInstanceProcAddr"}}true
-    {{else if eq $.Name "vkGetDeviceProcAddr"}}true
-
-    {{/* VK_KHR_swapchain v69 requirement */}}
-    {{else if eq $.Name "vkBindImageMemory2"}}true
-    {{else if eq $.Name "vkBindImageMemory2KHR"}}true
-    {{end}}
-
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      {{Macro "driver.IsExtensionIntercepted" $ext}}
-    {{end}}
-
-  {{end}}
-{{end}}
-
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits the ProcHook enum for core Vulkan API verions.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.GetProcHookEnum"}}
-  {{if GetAnnotation $ "vulkan1_1"}}ProcHook::EXTENSION_CORE_1_1
-  {{else}}ProcHook::EXTENSION_CORE_1_0
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if a function needs a ProcHook stub.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.NeedProcHookStub"}}
-  {{AssertType $ "Function"}}
-
-  {{if and (Macro "driver.IsIntercepted" $) (Macro "IsDeviceDispatched" $)}}
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      {{if not (Macro "IsExtensionInternal" $ext)}}true{{end}}
-    {{end}}
-
-    {{if GetAnnotation $ "vulkan1_1"}}true{{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits definition of struct ProcHook.
--------------------------------------------------------------------------------
-*/}}
-{{define "driver.C++.DefineProcHookType"}}
-  struct ProcHook {
-      enum Type {
-        GLOBAL,
-        INSTANCE,
-        DEVICE,
-      };
-
-      enum Extension {
-        {{$exts := Strings (Macro "driver.KnownExtensions") | SplitOn "\n"}}
-        {{range $e := $exts}}
-          {{TrimPrefix "VK_" $e}},
-        {{end}}
-        ¶
-        EXTENSION_CORE_1_0,
-        EXTENSION_CORE_1_1,
-        EXTENSION_COUNT,
-        EXTENSION_UNKNOWN,
-      };
-      ¶
-      const char* name;
-      Type type;
-      Extension extension;
-      ¶
-      PFN_vkVoidFunction proc;
-      PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks
-  };
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits INIT_PROC_EXT macro for vulkan::driver.
--------------------------------------------------------------------------------
-*/}}
-{{define "driver.C++.DefineInitProcExtMacro"}}
-  #define INIT_PROC_EXT(ext, required, obj, proc) do {          \
-      if (extensions[ProcHook::ext])                            \
-        INIT_PROC(required, obj, proc);                         \
-  } while(0)
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a stub for ProcHook::checked_proc.
--------------------------------------------------------------------------------
-*/}}
-{{define "driver.C++.DefineProcHookStub"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "driver.NeedProcHookStub" $)}}
-    {{$ext_name := Strings ("")}}
-    {{$ext_hook := Strings ("")}}
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      {{$ext_name = index $ext.Arguments 0}}
-      {{$ext_hook = Strings ("ProcHook::") (Macro "BaseName" $ext)}}
-    {{else}}
-      {{$ext_name = Strings ("VK_VERSION_1_0")}}
-      {{$ext_hook = (Macro "driver.GetProcHookEnum" $)}}
-    {{end}}
-
-    {{$base := (Macro "BaseName" $)}}
-
-    VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) {
-      {{$p0 := index $.CallParameters 0}}
-
-      if (GetData({{$p0.Name}}).hook_extensions[{{$ext_hook}}]) {
-        {{if not (IsVoid $.Return.Type)}}return §{{end}}
-        {{$base}}({{Macro "Arguments" $}});
-      } else {
-        Logger({{$p0.Name}}).Err({{$p0.Name}}, "{{$ext_name}} not enabled. {{$.Name}} not executed.");
-        {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}}
-      }
-    }
-    ¶
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits definition of a global ProcHook.
--------------------------------------------------------------------------------
-*/}}
-{{define "driver.C++.DefineGlobalProcHook"}}
-  {{AssertType $ "Function"}}
-
-  {{$base := (Macro "BaseName" $)}}
-
-  {{$ext := GetAnnotation $ "extension"}}
-  {{if $ext}}
-    {{Error "invalid global extension"}}
-  {{end}}
-
-  {
-    "{{$.Name}}",
-    ProcHook::GLOBAL,
-    {{Macro "driver.GetProcHookEnum" $}},
-    reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-    nullptr,
-  },
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits definition of an instance ProcHook.
--------------------------------------------------------------------------------
-*/}}
-{{define "driver.C++.DefineInstanceProcHook"}}
-  {{AssertType $ "Function"}}
-
-  {{$base := (Macro "BaseName" $)}}
-
-  {
-    "{{$.Name}}",
-    ProcHook::INSTANCE,
-
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      ProcHook::{{Macro "BaseName" $ext}},
-
-      {{if (Macro "IsExtensionInternal" $ext)}}
-        nullptr,
-        nullptr,
-      {{else}}
-        reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-        nullptr,
-      {{end}}
-    {{else}}
-      {{Macro "driver.GetProcHookEnum" $}},
-      reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-      nullptr,
-    {{end}}
-  },
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits definition of a device ProcHook.
--------------------------------------------------------------------------------
-*/}}
-{{define "driver.C++.DefineDeviceProcHook"}}
-  {{AssertType $ "Function"}}
-
-  {{$base := (Macro "BaseName" $)}}
-
-  {
-    "{{$.Name}}",
-    ProcHook::DEVICE,
-
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if or $ext (GetAnnotation $ "vulkan1_1")}}
-      {{if $ext}}
-        ProcHook::{{Macro "BaseName" $ext}},
-        {{if Macro "IsExtensionInternal" $ext}}
-          nullptr,
-          nullptr,
-        {{else}}
-          reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-          reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}),
-        {{end}}
-      {{else}}
-        {{Macro "driver.GetProcHookEnum" $}},
-        reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-        reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}),
-      {{end}}
-    {{else}}
-      {{Macro "driver.GetProcHookEnum" $}},
-      reinterpret_cast<PFN_vkVoidFunction>({{$base}}),
-      nullptr,
-    {{end}}
-  },
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits true if a function is needed by vulkan::driver.
--------------------------------------------------------------------------------
-*/}}
-{{define "driver.IsDriverTableEntry"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "IsFunctionSupported" $)}}
-    {{/* Create functions of dispatchable objects */}}
-    {{     if eq $.Name "vkCreateDevice"}}true
-    {{else if eq $.Name "vkGetDeviceQueue"}}true
-    {{else if eq $.Name "vkGetDeviceQueue2"}}true
-    {{else if eq $.Name "vkAllocateCommandBuffers"}}true
-
-    {{/* Destroy functions of dispatchable objects */}}
-    {{else if eq $.Name "vkDestroyInstance"}}true
-    {{else if eq $.Name "vkDestroyDevice"}}true
-
-    {{/* Enumeration of extensions */}}
-    {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true
-
-    {{/* We cache physical devices in loader.cpp */}}
-    {{else if eq $.Name "vkEnumeratePhysicalDevices"}}true
-    {{else if eq $.Name "vkEnumeratePhysicalDeviceGroups"}}true
-
-    {{else if eq $.Name "vkGetInstanceProcAddr"}}true
-    {{else if eq $.Name "vkGetDeviceProcAddr"}}true
-
-    {{/* VK_KHR_swapchain->VK_ANDROID_native_buffer translation */}}
-    {{else if eq $.Name "vkCreateImage"}}true
-    {{else if eq $.Name "vkDestroyImage"}}true
-
-    {{else if eq $.Name "vkGetPhysicalDeviceProperties"}}true
-    {{else if eq $.Name "vkGetPhysicalDeviceProperties2"}}true
-    {{else if eq $.Name "vkGetPhysicalDeviceProperties2KHR"}}true
-
-    {{/* VK_KHR_swapchain v69 requirement */}}
-    {{else if eq $.Name "vkBindImageMemory2"}}true
-    {{else if eq $.Name "vkBindImageMemory2KHR"}}true
-    {{end}}
-
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      {{$ext_name := index $ext.Arguments 0}}
-      {{     if eq $ext_name "VK_ANDROID_native_buffer"}}true
-      {{else if eq $ext_name "VK_EXT_debug_report"}}true
-      {{end}}
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if an instance-dispatched function is needed by vulkan::driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.IsInstanceDriverTableEntry"}}
-  {{AssertType $ "Function"}}
-
-  {{if and (Macro "driver.IsDriverTableEntry" $) (Macro "IsInstanceDispatched" $)}}
-    true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits true if a device-dispatched function is needed by vulkan::driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "driver.IsDeviceDriverTableEntry"}}
-  {{AssertType $ "Function"}}
-
-  {{if and (Macro "driver.IsDriverTableEntry" $) (Macro "IsDeviceDispatched" $)}}
-    true
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a function/extension name without the "vk"/"VK_" prefix.
--------------------------------------------------------------------------------
-*/}}
-{{define "BaseName"}}
-  {{     if IsFunction $}}{{TrimPrefix "vk" $.Name}}
-  {{else if eq $.Name "extension"}}{{TrimPrefix "VK_" (index $.Arguments 0)}}
-  {{else}}{{Error "invalid use of BaseName"}}
-  {{end}}
-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a comma-separated list of C parameter names for the given command.
--------------------------------------------------------------------------------
-*/}}
-{{define "Arguments"}}
-  {{AssertType $ "Function"}}
-
-  {{ForEach $.CallParameters "ParameterName" | JoinWith ", "}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-*/}}
-{{define "IsGloballyDispatched"}}
-  {{AssertType $ "Function"}}
-  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Global")}}
-    true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" for supported functions that undergo table dispatch. Only global
-  functions and functions handled in the loader top without calling into
-  lower layers are not dispatched.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsInstanceDispatched"}}
-  {{AssertType $ "Function"}}
-  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}}
-    true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" for supported functions that can have device-specific dispatch.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsDeviceDispatched"}}
-  {{AssertType $ "Function"}}
-  {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Device")}}
-    true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" if a function is core or from a supportable extension.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsFunctionSupported"}}
-  {{AssertType $ "Function"}}
-  {{if not (GetAnnotation $ "pfn")}}
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if not $ext}}true
-    {{else if not (Macro "IsExtensionBlacklisted" $ext)}}true
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Decides whether a function should be exported from the Android Vulkan
-  library. Functions in the core API and in loader extensions are exported.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsFunctionExported"}}
-  {{AssertType $ "Function"}}
-
-  {{if (Macro "IsFunctionSupported" $)}}
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      {{Macro "IsExtensionExported" $ext}}
-    {{else}}
-      true
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emit "true" if an extension is unsupportable on Android.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsExtensionBlacklisted"}}
-  {{$ext := index $.Arguments 0}}
-  {{     if eq $ext "VK_KHR_display"}}true
-  {{else if eq $ext "VK_KHR_display_swapchain"}}true
-  {{else if eq $ext "VK_KHR_mir_surface"}}true
-  {{else if eq $ext "VK_KHR_xcb_surface"}}true
-  {{else if eq $ext "VK_KHR_xlib_surface"}}true
-  {{else if eq $ext "VK_KHR_wayland_surface"}}true
-  {{else if eq $ext "VK_KHR_win32_surface"}}true
-  {{else if eq $ext "VK_KHR_external_memory_win32"}}true
-  {{else if eq $ext "VK_KHR_win32_keyed_mutex"}}true
-  {{else if eq $ext "VK_KHR_external_semaphore_win32"}}true
-  {{else if eq $ext "VK_KHR_external_fence_win32"}}true
-  {{else if eq $ext "VK_EXT_acquire_xlib_display"}}true
-  {{else if eq $ext "VK_EXT_direct_mode_display"}}true
-  {{else if eq $ext "VK_EXT_display_surface_counter"}}true
-  {{else if eq $ext "VK_EXT_display_control"}}true
-  {{else if eq $ext "VK_FUCHSIA_imagepipe_surface"}}true
-  {{else if eq $ext "VK_MVK_ios_surface"}}true
-  {{else if eq $ext "VK_MVK_macos_surface"}}true
-  {{else if eq $ext "VK_NN_vi_surface"}}true
-  {{else if eq $ext "VK_NV_external_memory_win32"}}true
-  {{else if eq $ext "VK_NV_win32_keyed_mutex"}}true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Reports whether an extension has functions exported by the loader.
-  E.g. applications can directly link to an extension function.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsExtensionExported"}}
-  {{$ext := index $.Arguments 0}}
-  {{     if eq $ext "VK_KHR_surface"}}true
-  {{else if eq $ext "VK_KHR_swapchain"}}true
-  {{else if eq $ext "VK_KHR_android_surface"}}true
-  {{else if eq $ext "VK_ANDROID_external_memory_android_hardware_buffer"}}true
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Reports whether an extension is internal to the loader and drivers,
-  so the loader should not enumerate it.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsExtensionInternal"}}
-  {{$ext := index $.Arguments 0}}
-  {{     if eq $ext "VK_ANDROID_native_buffer"}}true
-  {{end}}
-{{end}}
diff --git a/vulkan/libvulkan/debug_report.h b/vulkan/libvulkan/debug_report.h
index 3d8bd50..e5b1587 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -78,7 +78,7 @@
         VkDebugReportCallbackEXT driver_handle;
     };
 
-    // TODO(jessehall): replace with std::shared_mutex when available in libc++
+    // TODO(b/143295577): use std::shared_mutex when available in libc++
     mutable std::shared_timed_mutex rwmutex_;
     Node head_;
 };
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 73ba92c..a7ec4ae 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -16,43 +16,38 @@
 
 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
 
+#include "driver.h"
+
+#include <dlfcn.h>
 #include <malloc.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/prctl.h>
 
-#include <dlfcn.h>
+#include <android/dlext.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android-base/properties.h>
+#include <configstore/Utils.h>
+#include <cutils/properties.h>
+#include <graphicsenv/GraphicsEnv.h>
+#include <log/log.h>
+#include <nativeloader/dlext_namespaces.h>
+#include <sys/prctl.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+
 #include <algorithm>
 #include <array>
 #include <climits>
 #include <new>
+#include <string_view>
 #include <sstream>
-#include <string>
+#include <vector>
 
-#include <log/log.h>
-
-#include <android/dlext.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <configstore/Utils.h>
-#include <cutils/properties.h>
-#include <graphicsenv/GraphicsEnv.h>
-#include <utils/Timers.h>
-#include <utils/Trace.h>
-#include <utils/Vector.h>
-
-#include "android-base/properties.h"
-
-#include "driver.h"
 #include "stubhal.h"
 
 using namespace android::hardware::configstore;
 using namespace android::hardware::configstore::V1_0;
 
-// TODO(b/37049319) Get this from a header once one exists
-extern "C" {
-android_namespace_t* android_get_exported_namespace(const char*);
-}
-
 // #define ENABLE_ALLOC_CALLSTACKS 1
 #if ENABLE_ALLOC_CALLSTACKS
 #include <utils/CallStack.h>
@@ -169,6 +164,11 @@
     "ro.board.platform",
 }};
 
+// LoadDriver returns:
+// * 0 when succeed, or
+// * -ENOENT when fail to open binary libraries, or
+// * -EINVAL when fail to find HAL_MODULE_INFO_SYM_AS_STR or
+//   HWVULKAN_HARDWARE_MODULE_ID in the library.
 int LoadDriver(android_namespace_t* library_namespace,
                const hwvulkan_module_t** module) {
     ATRACE_CALL();
@@ -214,7 +214,7 @@
     if (!ns)
         return -ENOENT;
     android::GraphicsEnv::getInstance().setDriverToLoad(
-        android::GraphicsEnv::Driver::VULKAN);
+        android::GpuStatsInfo::Driver::VULKAN);
     return LoadDriver(ns, module);
 }
 
@@ -225,8 +225,14 @@
     if (!ns)
         return -ENOENT;
     android::GraphicsEnv::getInstance().setDriverToLoad(
-        android::GraphicsEnv::Driver::VULKAN_UPDATED);
-    return LoadDriver(ns, module);
+        android::GpuStatsInfo::Driver::VULKAN_UPDATED);
+    int result = LoadDriver(ns, module);
+    if (result != 0) {
+        LOG_ALWAYS_FATAL(
+            "couldn't find an updated Vulkan implementation from %s",
+            android::GraphicsEnv::getInstance().getDriverPath().c_str());
+    }
+    return result;
 }
 
 bool Hal::Open() {
@@ -260,7 +266,7 @@
     }
     if (result != 0) {
         android::GraphicsEnv::getInstance().setDriverLoaded(
-            android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime);
+            android::GpuStatsInfo::Api::API_VK, false, systemTime() - openTime);
         ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result);
         return true;
     }
@@ -274,7 +280,7 @@
     ATRACE_END();
     if (result != 0) {
         android::GraphicsEnv::getInstance().setDriverLoaded(
-            android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime);
+            android::GpuStatsInfo::Api::API_VK, false, systemTime() - openTime);
         // Any device with a Vulkan HAL should be able to open the device.
         ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result),
               result);
@@ -286,7 +292,7 @@
     hal_.InitDebugReportIndex();
 
     android::GraphicsEnv::getInstance().setDriverLoaded(
-        android::GraphicsEnv::Api::API_VK, true, systemTime() - openTime);
+        android::GpuStatsInfo::Api::API_VK, true, systemTime() - openTime);
 
     return true;
 }
@@ -652,13 +658,16 @@
 
 void CreateInfoWrapper::UpgradeDeviceCoreApiVersion(uint32_t api_version) {
     ALOG_ASSERT(!is_instance_, "Device only API called by instance wrapper.");
+
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wold-style-cast"
     api_version ^= VK_VERSION_PATCH(api_version);
 #pragma clang diagnostic pop
+
     // cap the API version to the loader supported highest version
     if (api_version > VK_API_VERSION_1_1)
         api_version = VK_API_VERSION_1_1;
+
     switch (api_version) {
         case VK_API_VERSION_1_1:
             hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1);
@@ -695,7 +704,7 @@
         return nullptr;
     }
 
-    // TODO(jessehall): Right now we never shrink allocations; if the new
+    // TODO(b/143295633): Right now we never shrink allocations; if the new
     // request is smaller than the existing chunk, we just continue using it.
     // Right now the loader never reallocs, so this doesn't matter. If that
     // changes, or if this code is copied into some other project, this should
@@ -836,8 +845,7 @@
     const char* pLayerName,
     uint32_t* pPropertyCount,
     VkExtensionProperties* pProperties) {
-
-    android::Vector<VkExtensionProperties> loader_extensions;
+    std::vector<VkExtensionProperties> loader_extensions;
     loader_extensions.push_back({
         VK_KHR_SURFACE_EXTENSION_NAME,
         VK_KHR_SURFACE_SPEC_VERSION});
@@ -860,7 +868,7 @@
         uint32_t count = std::min(
             *pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
 
-        std::copy_n(loader_extensions.begin(), count, pProperties);
+        std::copy_n(loader_extensions.data(), count, pProperties);
 
         if (count < loader_extensions.size()) {
             *pPropertyCount = count;
@@ -906,8 +914,7 @@
 
 bool QueryPresentationProperties(
     VkPhysicalDevice physicalDevice,
-    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties)
-{
+    VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) {
     const InstanceData& data = GetData(physicalDevice);
 
     // GPDP2 must be present and enabled on the instance.
@@ -947,7 +954,7 @@
     VkExtensionProperties* pProperties) {
     const InstanceData& data = GetData(physicalDevice);
     // extensions that are unconditionally exposed by the loader
-    android::Vector<VkExtensionProperties> loader_extensions;
+    std::vector<VkExtensionProperties> loader_extensions;
     loader_extensions.push_back({
         VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME,
         VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION});
@@ -983,7 +990,7 @@
         uint32_t count = std::min(
             *pPropertyCount, static_cast<uint32_t>(loader_extensions.size()));
 
-        std::copy_n(loader_extensions.begin(), count, pProperties);
+        std::copy_n(loader_extensions.data(), count, pProperties);
 
         if (count < loader_extensions.size()) {
             *pPropertyCount = count;
@@ -1205,11 +1212,10 @@
     if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
         // Log that the app is hitting software Vulkan implementation
         android::GraphicsEnv::getInstance().setTargetStats(
-            android::GraphicsEnv::Stats::CPU_VULKAN_IN_USE);
+            android::GpuStatsInfo::Stats::CPU_VULKAN_IN_USE);
     }
 
     data->driver_device = dev;
-    data->driver_version = properties.driverVersion;
 
     *pDevice = dev;
 
@@ -1274,11 +1280,10 @@
         if (!device_count)
             return VK_INCOMPLETE;
 
-        android::Vector<VkPhysicalDevice> devices;
-        devices.resize(device_count);
+        std::vector<VkPhysicalDevice> devices(device_count);
         *pPhysicalDeviceGroupCount = device_count;
-        result = EnumeratePhysicalDevices(instance, &device_count,
-                                          devices.editArray());
+        result =
+            EnumeratePhysicalDevices(instance, &device_count, devices.data());
         if (result < 0)
             return result;
 
@@ -1349,5 +1354,16 @@
     return result;
 }
 
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue,
+                                uint32_t submitCount,
+                                const VkSubmitInfo* pSubmits,
+                                VkFence fence) {
+    ATRACE_CALL();
+
+    const auto& data = GetData(queue);
+
+    return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence);
+}
+
 }  // namespace driver
 }  // namespace vulkan
diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h
index ffe2a8b..23c717c 100644
--- a/vulkan/libvulkan/driver.h
+++ b/vulkan/libvulkan/driver.h
@@ -98,7 +98,6 @@
 
     VkDevice driver_device;
     DeviceDriverTable driver;
-    uint32_t driver_version;
 };
 
 bool OpenHAL();
@@ -126,6 +125,7 @@
 VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue);
 VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
 VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers);
+VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
 // clang-format on
 
 template <typename DispatchableType>
diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp
index aa31735..52205e9 100644
--- a/vulkan/libvulkan/driver_gen.cpp
+++ b/vulkan/libvulkan/driver_gen.cpp
@@ -16,12 +16,11 @@
 
 // WARNING: This file is generated. See ../README.md for instructions.
 
+#include <log/log.h>
 #include <string.h>
 
 #include <algorithm>
 
-#include <log/log.h>
-
 #include "driver.h"
 
 namespace vulkan {
@@ -31,23 +30,6 @@
 
 // clang-format off
 
-VKAPI_ATTR VkResult checkedBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
-    if (GetData(device).hook_extensions[ProcHook::EXTENSION_CORE_1_1]) {
-        return BindImageMemory2(device, bindInfoCount, pBindInfos);
-    } else {
-        Logger(device).Err(device, "VK_VERSION_1_0 not enabled. vkBindImageMemory2 not executed.");
-        return VK_SUCCESS;
-    }
-}
-
-VKAPI_ATTR void checkedGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) {
-    if (GetData(device).hook_extensions[ProcHook::EXTENSION_CORE_1_1]) {
-        GetDeviceQueue2(device, pQueueInfo, pQueue);
-    } else {
-        Logger(device).Err(device, "VK_VERSION_1_0 not enabled. vkGetDeviceQueue2 not executed.");
-    }
-}
-
 VKAPI_ATTR VkResult checkedCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) {
     if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
         return CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
@@ -92,6 +74,24 @@
     }
 }
 
+VKAPI_ATTR VkResult checkedBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
+    if (GetData(device).hook_extensions[ProcHook::EXTENSION_CORE_1_1]) {
+        return BindImageMemory2(device, bindInfoCount, pBindInfos);
+    } else {
+        Logger(device).Err(device, "VK_VERSION_1_1 not enabled. vkBindImageMemory2 not executed.");
+        return VK_SUCCESS;
+    }
+}
+
+VKAPI_ATTR VkResult checkedBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_bind_memory2]) {
+        return BindImageMemory2KHR(device, bindInfoCount, pBindInfos);
+    } else {
+        Logger(device).Err(device, "VK_KHR_bind_memory2 not enabled. vkBindImageMemory2KHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
 VKAPI_ATTR VkResult checkedGetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
     if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) {
         return GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities);
@@ -119,6 +119,23 @@
     }
 }
 
+VKAPI_ATTR void checkedSetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata) {
+    if (GetData(device).hook_extensions[ProcHook::EXT_hdr_metadata]) {
+        SetHdrMetadataEXT(device, swapchainCount, pSwapchains, pMetadata);
+    } else {
+        Logger(device).Err(device, "VK_EXT_hdr_metadata not enabled. vkSetHdrMetadataEXT not executed.");
+    }
+}
+
+VKAPI_ATTR VkResult checkedGetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain) {
+    if (GetData(device).hook_extensions[ProcHook::KHR_shared_presentable_image]) {
+        return GetSwapchainStatusKHR(device, swapchain);
+    } else {
+        Logger(device).Err(device, "VK_KHR_shared_presentable_image not enabled. vkGetSwapchainStatusKHR not executed.");
+        return VK_SUCCESS;
+    }
+}
+
 VKAPI_ATTR VkResult checkedGetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) {
     if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) {
         return GetRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties);
@@ -137,29 +154,11 @@
     }
 }
 
-VKAPI_ATTR void checkedSetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata) {
-    if (GetData(device).hook_extensions[ProcHook::EXT_hdr_metadata]) {
-        SetHdrMetadataEXT(device, swapchainCount, pSwapchains, pMetadata);
+VKAPI_ATTR void checkedGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) {
+    if (GetData(device).hook_extensions[ProcHook::EXTENSION_CORE_1_1]) {
+        GetDeviceQueue2(device, pQueueInfo, pQueue);
     } else {
-        Logger(device).Err(device, "VK_EXT_hdr_metadata not enabled. vkSetHdrMetadataEXT not executed.");
-    }
-}
-
-VKAPI_ATTR VkResult checkedGetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR swapchain) {
-    if (GetData(device).hook_extensions[ProcHook::KHR_shared_presentable_image]) {
-        return GetSwapchainStatusKHR(device, swapchain);
-    } else {
-        Logger(device).Err(device, "VK_KHR_shared_presentable_image not enabled. vkGetSwapchainStatusKHR not executed.");
-        return VK_SUCCESS;
-    }
-}
-
-VKAPI_ATTR VkResult checkedBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHR* pBindInfos) {
-    if (GetData(device).hook_extensions[ProcHook::KHR_bind_memory2]) {
-        return BindImageMemory2KHR(device, bindInfoCount, pBindInfos);
-    } else {
-        Logger(device).Err(device, "VK_KHR_bind_memory2 not enabled. vkBindImageMemory2KHR not executed.");
-        return VK_SUCCESS;
+        Logger(device).Err(device, "VK_VERSION_1_1 not enabled. vkGetDeviceQueue2 not executed.");
     }
 }
 
@@ -462,6 +461,13 @@
         nullptr,
     },
     {
+        "vkQueueSubmit",
+        ProcHook::DEVICE,
+        ProcHook::EXTENSION_CORE_1_0,
+        reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit),
+        nullptr,
+    },
+    {
         "vkSetHdrMetadataEXT",
         ProcHook::DEVICE,
         ProcHook::EXT_hdr_metadata,
@@ -491,14 +497,14 @@
     if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace;
     if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing;
     if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface;
+    if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2;
     if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present;
     if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image;
     if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface;
     if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain;
-    if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2;
-    if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
     if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer;
     if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2;
+    if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2;
     // clang-format on
     return ProcHook::EXTENSION_UNKNOWN;
 }
@@ -534,12 +540,12 @@
     INIT_PROC(true, instance, GetPhysicalDeviceProperties);
     INIT_PROC(true, instance, CreateDevice);
     INIT_PROC(true, instance, EnumerateDeviceExtensionProperties);
-    INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
-    INIT_PROC(false, instance, GetPhysicalDeviceProperties2);
     INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT);
     INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT);
     INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT);
+    INIT_PROC(false, instance, GetPhysicalDeviceProperties2);
     INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR);
+    INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups);
     // clang-format on
 
     return success;
@@ -555,16 +561,17 @@
     INIT_PROC(true, dev, GetDeviceProcAddr);
     INIT_PROC(true, dev, DestroyDevice);
     INIT_PROC(true, dev, GetDeviceQueue);
+    INIT_PROC(true, dev, QueueSubmit);
     INIT_PROC(true, dev, CreateImage);
     INIT_PROC(true, dev, DestroyImage);
     INIT_PROC(true, dev, AllocateCommandBuffers);
     INIT_PROC(false, dev, BindImageMemory2);
+    INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR);
     INIT_PROC(false, dev, GetDeviceQueue2);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID);
     INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID);
-    INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR);
     // clang-format on
 
     return success;
@@ -572,5 +579,3 @@
 
 }  // namespace driver
 }  // namespace vulkan
-
-// clang-format on
diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h
index 831efb7..43c4d14 100644
--- a/vulkan/libvulkan/driver_gen.h
+++ b/vulkan/libvulkan/driver_gen.h
@@ -21,6 +21,7 @@
 
 #include <vulkan/vk_android_native_buffer.h>
 #include <vulkan/vulkan.h>
+
 #include <bitset>
 
 namespace vulkan {
@@ -39,14 +40,14 @@
         EXT_swapchain_colorspace,
         GOOGLE_display_timing,
         KHR_android_surface,
+        KHR_get_surface_capabilities2,
         KHR_incremental_present,
         KHR_shared_presentable_image,
         KHR_surface,
         KHR_swapchain,
-        KHR_get_surface_capabilities2,
-        KHR_get_physical_device_properties2,
         ANDROID_external_memory_android_hardware_buffer,
         KHR_bind_memory2,
+        KHR_get_physical_device_properties2,
 
         EXTENSION_CORE_1_0,
         EXTENSION_CORE_1_1,
@@ -70,12 +71,12 @@
     PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties;
     PFN_vkCreateDevice CreateDevice;
     PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties;
-    PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
-    PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2;
     PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT;
     PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT;
     PFN_vkDebugReportMessageEXT DebugReportMessageEXT;
+    PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2;
     PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR;
+    PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups;
     // clang-format on
 };
 
@@ -84,16 +85,17 @@
     PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
     PFN_vkDestroyDevice DestroyDevice;
     PFN_vkGetDeviceQueue GetDeviceQueue;
+    PFN_vkQueueSubmit QueueSubmit;
     PFN_vkCreateImage CreateImage;
     PFN_vkDestroyImage DestroyImage;
     PFN_vkAllocateCommandBuffers AllocateCommandBuffers;
     PFN_vkBindImageMemory2 BindImageMemory2;
+    PFN_vkBindImageMemory2KHR BindImageMemory2KHR;
     PFN_vkGetDeviceQueue2 GetDeviceQueue2;
     PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID;
     PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID;
     PFN_vkAcquireImageANDROID AcquireImageANDROID;
     PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID;
-    PFN_vkBindImageMemory2KHR BindImageMemory2KHR;
     // clang-format on
 };
 
diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp
index 7326692..a14fed2 100644
--- a/vulkan/libvulkan/layers_extensions.cpp
+++ b/vulkan/libvulkan/layers_extensions.cpp
@@ -24,7 +24,6 @@
 #include <string.h>
 #include <sys/prctl.h>
 
-#include <memory>
 #include <mutex>
 #include <string>
 #include <vector>
@@ -39,13 +38,7 @@
 #include <utils/Trace.h>
 #include <ziparchive/zip_archive.h>
 
-// TODO(jessehall): The whole way we deal with extensions is pretty hokey, and
-// not a good long-term solution. Having a hard-coded enum of extensions is
-// bad, of course. Representing sets of extensions (requested, supported, etc.)
-// as a bitset isn't necessarily bad, if the mapping from extension to bit were
-// dynamic. Need to rethink this completely when there's a little more time.
-
-// TODO(jessehall): This file currently builds up global data structures as it
+// TODO(b/143296676): This file currently builds up global data structures as it
 // loads, and never cleans them up. This means we're doing heap allocations
 // without going through an app-provided allocator, but worse, we'll leak those
 // allocations if the loader is unloaded.
@@ -225,10 +218,16 @@
     }
 
     // get layer properties
-    auto properties = std::make_unique<VkLayerProperties[]>(num_instance_layers + num_device_layers);
+    std::vector<VkLayerProperties> properties(num_instance_layers + num_device_layers);
+    result = enumerate_instance_layers(&num_instance_layers, properties.data());
+    if (result != VK_SUCCESS) {
+        ALOGE("vkEnumerateInstanceLayerProperties failed for library '%s': %d",
+              path_.c_str(), result);
+        return false;
+    }
     if (num_device_layers > 0) {
         result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers,
-                                         properties.get() + num_instance_layers);
+                                         &properties[num_instance_layers]);
         if (result != VK_SUCCESS) {
             ALOGE(
                 "vkEnumerateDeviceLayerProperties failed for library '%s': %d",
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 1419119..a44b9e7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -18,13 +18,14 @@
 
 #include <android/hardware/graphics/common/1.0/types.h>
 #include <grallocusage/GrallocUsageConversion.h>
+#include <graphicsenv/GraphicsEnv.h>
 #include <log/log.h>
 #include <sync/sync.h>
 #include <system/window.h>
 #include <ui/BufferQueueDefs.h>
 #include <utils/StrongPointer.h>
+#include <utils/Timers.h>
 #include <utils/Trace.h>
-#include <utils/Vector.h>
 
 #include <algorithm>
 #include <unordered_set>
@@ -34,10 +35,6 @@
 
 using android::hardware::graphics::common::V1_0::BufferUsage;
 
-// TODO(jessehall): Currently we don't have a good error code for when a native
-// window operation fails. Just returning INITIALIZATION_FAILED for now. Later
-// versions (post SDK 0.9) of the API/extension have a better error code.
-// When updating to that version, audit all error returns.
 namespace vulkan {
 namespace driver {
 
@@ -48,29 +45,12 @@
     VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR |
     VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR |
     VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR |
-    // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
-    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR |
-    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
-    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR |
-    // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR |
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR |
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR |
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR |
+    VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR |
     VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR;
 
-int TranslateVulkanToNativeTransform(VkSurfaceTransformFlagBitsKHR transform) {
-    switch (transform) {
-        // TODO: See TODO in TranslateNativeToVulkanTransform
-        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
-            return NATIVE_WINDOW_TRANSFORM_ROT_90;
-        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
-            return NATIVE_WINDOW_TRANSFORM_ROT_180;
-        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
-            return NATIVE_WINDOW_TRANSFORM_ROT_270;
-        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
-        case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
-        default:
-            return 0;
-    }
-}
-
 VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) {
     // Native and Vulkan transforms are isomorphic, but are represented
     // differently. Vulkan transforms are built up of an optional horizontal
@@ -78,27 +58,22 @@
     // transforms are built up from a horizontal flip, vertical flip, and
     // 90-degree rotation, all optional but always in that order.
 
-    // TODO(jessehall): For now, only support pure rotations, not
-    // flip or flip-and-rotate, until I have more time to test them and build
-    // sample code. As far as I know we never actually use anything besides
-    // pure rotations anyway.
-
     switch (native) {
-        case 0:  // 0x0
+        case 0:
             return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-        // case NATIVE_WINDOW_TRANSFORM_FLIP_H:  // 0x1
-        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR;
-        // case NATIVE_WINDOW_TRANSFORM_FLIP_V:  // 0x2
-        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR;
-        case NATIVE_WINDOW_TRANSFORM_ROT_180:  // FLIP_H | FLIP_V
+        case NATIVE_WINDOW_TRANSFORM_FLIP_H:
+            return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_FLIP_V:
+            return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:
             return VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR;
-        case NATIVE_WINDOW_TRANSFORM_ROT_90:  // 0x4
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:
             return VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR;
-        // case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90:
-        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR;
-        // case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90:
-        //     return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
-        case NATIVE_WINDOW_TRANSFORM_ROT_270:  // FLIP_H | FLIP_V | ROT_90
+        case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90:
+            return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90:
+            return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR;
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:
             return VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR;
         case NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY:
         default:
@@ -106,6 +81,31 @@
     }
 }
 
+int TranslateVulkanToNativeTransform(VkSurfaceTransformFlagBitsKHR transform) {
+    switch (transform) {
+        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_180;
+        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_270;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_H;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_H |
+                   NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_V;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_V |
+                   NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+        case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
+        default:
+            return 0;
+    }
+}
+
 int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) {
     switch (transform) {
         case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
@@ -114,17 +114,16 @@
             return NATIVE_WINDOW_TRANSFORM_ROT_180;
         case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
             return NATIVE_WINDOW_TRANSFORM_ROT_90;
-        // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform.
-        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
-        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H;
-        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
-        //     return NATIVE_WINDOW_TRANSFORM_FLIP_H |
-        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
-        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
-        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V;
-        // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
-        //     return NATIVE_WINDOW_TRANSFORM_FLIP_V |
-        //            NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_H;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_H |
+                   NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_V;
+        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_FLIP_V |
+                   NATIVE_WINDOW_TRANSFORM_ROT_90;
         case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
         case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
         default:
@@ -134,7 +133,6 @@
 
 class TimingInfo {
    public:
-    TimingInfo() = default;
     TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId)
         : vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
           native_frame_id_(nativeFrameId) {}
@@ -201,8 +199,6 @@
             { NATIVE_WINDOW_TIMESTAMP_PENDING };
 };
 
-// ----------------------------------------------------------------------------
-
 struct Surface {
     android::sp<ANativeWindow> window;
     VkSwapchainKHR swapchain_handle;
@@ -233,8 +229,10 @@
           mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
           pre_transform(pre_transform_),
           frame_timestamps_enabled(false),
+          acquire_next_image_timeout(-1),
           shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
-                 present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+                 present_mode ==
+                     VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
         ANativeWindow* window = surface.window.get();
         native_window_get_refresh_cycle_duration(
             window,
@@ -256,6 +254,7 @@
     int pre_transform;
     bool frame_timestamps_enabled;
     int64_t refresh_duration;
+    nsecs_t acquire_next_image_timeout;
     bool shared;
 
     struct Image {
@@ -270,7 +269,7 @@
         bool dequeued;
     } images[android::BufferQueueDefs::NUM_BUFFER_SLOTS];
 
-    android::Vector<TimingInfo> timing;
+    std::vector<TimingInfo> timing;
 };
 
 VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
@@ -285,6 +284,8 @@
                            ANativeWindow* window,
                            int release_fence,
                            Swapchain::Image& image) {
+    ATRACE_CALL();
+
     ALOG_ASSERT(release_fence == -1 || image.dequeued,
                 "ReleaseSwapchainImage: can't provide a release fence for "
                 "non-dequeued images");
@@ -323,7 +324,9 @@
     }
 
     if (image.image) {
+        ATRACE_BEGIN("DestroyImage");
         GetData(device).driver.DestroyImage(device, image.image, nullptr);
+        ATRACE_END();
         image.image = VK_NULL_HANDLE;
     }
 
@@ -349,7 +352,7 @@
     uint32_t num_ready = 0;
     const size_t num_timings = swapchain.timing.size() - MIN_NUM_FRAMES_AGO + 1;
     for (uint32_t i = 0; i < num_timings; i++) {
-        TimingInfo& ti = swapchain.timing.editItemAt(i);
+        TimingInfo& ti = swapchain.timing[i];
         if (ti.ready()) {
             // This TimingInfo is ready to be reported to the user.  Add it
             // to the num_ready.
@@ -364,21 +367,18 @@
         int64_t composition_latch_time = 0;
         int64_t actual_present_time = 0;
         // Obtain timestamps:
-        int ret = native_window_get_frame_timestamps(
+        int err = native_window_get_frame_timestamps(
             swapchain.surface.window.get(), ti.native_frame_id_,
             &desired_present_time, &render_complete_time,
             &composition_latch_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,
             nullptr,  //&dequeue_ready_time,
             nullptr /*&reads_done_time*/);
 
-        if (ret != android::NO_ERROR) {
+        if (err != android::OK) {
             continue;
         }
 
@@ -400,7 +400,6 @@
     return num_ready;
 }
 
-// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!!
 void copy_ready_timings(Swapchain& swapchain,
                         uint32_t* count,
                         VkPastPresentationTimingGOOGLE* timings) {
@@ -419,7 +418,7 @@
     }
 
     uint32_t num_copied = 0;
-    size_t num_to_remove = 0;
+    int32_t num_to_remove = 0;
     for (uint32_t i = 0; i <= last_ready && num_copied < *count; i++) {
         const TimingInfo& ti = swapchain.timing[i];
         if (ti.ready()) {
@@ -431,7 +430,8 @@
 
     // Discard old frames that aren't ready if newer frames are ready.
     // We don't expect to get the timing info for those old frames.
-    swapchain.timing.removeItemsAt(0, num_to_remove);
+    swapchain.timing.erase(swapchain.timing.begin(),
+                           swapchain.timing.begin() + num_to_remove);
 
     *count = num_copied;
 }
@@ -534,20 +534,17 @@
     surface->swapchain_handle = VK_NULL_HANDLE;
     int err = native_window_get_consumer_usage(surface->window.get(),
                                                &surface->consumer_usage);
-    if (err != android::NO_ERROR) {
+    if (err != android::OK) {
         ALOGE("native_window_get_consumer_usage() failed: %s (%d)",
               strerror(-err), err);
         surface->~Surface();
         allocator->pfnFree(allocator->pUserData, surface);
-        return VK_ERROR_INITIALIZATION_FAILED;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
     err =
         native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    if (err != android::OK) {
         ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
               err);
         surface->~Surface();
@@ -595,7 +592,7 @@
 
     int query_value;
     int err = window->query(window, NATIVE_WINDOW_FORMAT, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_FORMAT query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -636,13 +633,13 @@
 
     int width, height;
     err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -650,16 +647,15 @@
 
     int transform_hint;
     err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    // TODO(jessehall): Figure out what the min/max values should be.
     int max_buffer_count;
     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count);
-    if (err != 0) {
+    if (err != android::OK) {
         ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -670,8 +666,7 @@
     capabilities->currentExtent =
         VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
 
-    // TODO(jessehall): Figure out what the max extent should be. Maximum
-    // texture dimension maybe?
+    // TODO(http://b/134182502): Figure out what the max extent should be.
     capabilities->minImageExtent = VkExtent2D{1, 1};
     capabilities->maxImageExtent = VkExtent2D{4096, 4096};
 
@@ -685,11 +680,6 @@
     // associated with the bufferqueue. It can't be changed from here.
     capabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
 
-    // TODO(jessehall): I think these are right, but haven't thought hard about
-    // it. Do we need to query the driver for support of any of these?
-    // Currently not included:
-    // - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not
-    // - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not
     capabilities->supportedUsageFlags =
         VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
         VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
@@ -713,8 +703,7 @@
     int err = native_window_get_wide_color_support(surface.window.get(),
                                                    &wide_color_support);
     if (err) {
-        // Not allowed to return a more sensible error code, so do this
-        return VK_ERROR_OUT_OF_HOST_MEMORY;
+        return VK_ERROR_SURFACE_LOST_KHR;
     }
     ALOGV("wide_color_support is: %d", wide_color_support);
     wide_color_support =
@@ -841,11 +830,10 @@
     } else {
         // temp vector for forwarding; we'll marshal it into the pSurfaceFormats
         // after the call.
-        android::Vector<VkSurfaceFormatKHR> surface_formats;
-        surface_formats.resize(*pSurfaceFormatCount);
+        std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount);
         VkResult result = GetPhysicalDeviceSurfaceFormatsKHR(
             physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount,
-            &surface_formats.editItemAt(0));
+            surface_formats.data());
 
         if (result == VK_SUCCESS || result == VK_INCOMPLETE) {
             // marshal results individually due to stride difference.
@@ -872,7 +860,7 @@
     ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
 
     err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -880,14 +868,14 @@
     uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
 
     err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
-    if (err != 0 || query_value < 0) {
+    if (err != android::OK || query_value < 0) {
         ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
               strerror(-err), err, query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
     uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
 
-    android::Vector<VkPresentModeKHR> present_modes;
+    std::vector<VkPresentModeKHR> present_modes;
     if (min_undequeued_buffers + 1 < max_buffer_count)
         present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
     present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
@@ -907,7 +895,7 @@
         if (*count < num_modes)
             result = VK_INCOMPLETE;
         *count = std::min(*count, num_modes);
-        std::copy(present_modes.begin(), present_modes.begin() + int(*count), modes);
+        std::copy_n(present_modes.data(), *count, modes);
     } else {
         *count = num_modes;
     }
@@ -972,17 +960,17 @@
 
         int width = 0, height = 0;
         err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
-        if (err != 0) {
+        if (err != android::OK) {
             ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
                   strerror(-err), err);
         }
         err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
-        if (err != 0) {
+        if (err != android::OK) {
             ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
                   strerror(-err), err);
         }
 
-        // TODO: Return something better than "whole window"
+        // TODO(b/143294545): Return something better than "whole window"
         pRects[0].offset.x = 0;
         pRects[0].offset.y = 0;
         pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width),
@@ -991,6 +979,40 @@
     return VK_SUCCESS;
 }
 
+static void DestroySwapchainInternal(VkDevice device,
+                                     VkSwapchainKHR swapchain_handle,
+                                     const VkAllocationCallbacks* allocator) {
+    ATRACE_CALL();
+
+    const auto& dispatch = GetData(device).driver;
+    Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
+    if (!swapchain) {
+        return;
+    }
+
+    bool active = swapchain->surface.swapchain_handle == swapchain_handle;
+    ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
+
+    if (window && swapchain->frame_timestamps_enabled) {
+        native_window_enable_frame_timestamps(window, false);
+    }
+
+    for (uint32_t i = 0; i < swapchain->num_images; i++) {
+        ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
+    }
+
+    if (active) {
+        swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
+    }
+
+    if (!allocator) {
+        allocator = &GetData(device).allocator;
+    }
+
+    swapchain->~Swapchain();
+    allocator->pfnFree(allocator->pUserData, swapchain);
+}
+
 VKAPI_ATTR
 VkResult CreateSwapchainKHR(VkDevice device,
                             const VkSwapchainCreateInfoKHR* create_info,
@@ -1065,17 +1087,25 @@
     // non-FREE state at any given time. Disconnecting and re-connecting
     // orphans the previous buffers, getting us back to the state where we can
     // dequeue all buffers.
-    err = native_window_api_disconnect(surface.window.get(),
-                                       NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+    //
+    // TODO(http://b/134186185) recycle swapchain images more efficiently
+    ANativeWindow* window = surface.window.get();
+    err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != android::OK, "native_window_api_disconnect failed: %s (%d)",
              strerror(-err), err);
-    err =
-        native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
-    ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+    err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
              strerror(-err), err);
 
-    err = native_window_set_buffer_count(surface.window.get(), 0);
-    if (err != 0) {
+    err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, -1);
+    if (err != android::OK) {
+        ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
+    err = native_window_set_buffer_count(window, 0);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1083,24 +1113,22 @@
 
     int swap_interval =
         create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
-    err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    err = window->setSwapInterval(window, swap_interval);
+    if (err != android::OK) {
         ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = native_window_set_shared_buffer_mode(surface.window.get(), false);
-    if (err != 0) {
+    err = native_window_set_shared_buffer_mode(window, false);
+    if (err != android::OK) {
         ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
-    err = native_window_set_auto_refresh(surface.window.get(), false);
-    if (err != 0) {
+    err = native_window_set_auto_refresh(window, false);
+    if (err != android::OK) {
         ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1110,31 +1138,23 @@
 
     const auto& dispatch = GetData(device).driver;
 
-    err = native_window_set_buffers_format(surface.window.get(),
-                                           native_pixel_format);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    err = native_window_set_buffers_format(window, native_pixel_format);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
               native_pixel_format, strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
-    err = native_window_set_buffers_data_space(surface.window.get(),
-                                               native_dataspace);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    err = native_window_set_buffers_data_space(window, native_dataspace);
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
               native_dataspace, strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
     err = native_window_set_buffers_dimensions(
-        surface.window.get(), static_cast<int>(create_info->imageExtent.width),
+        window, static_cast<int>(create_info->imageExtent.width),
         static_cast<int>(create_info->imageExtent.height));
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
               create_info->imageExtent.width, create_info->imageExtent.height,
               strerror(-err), err);
@@ -1150,11 +1170,8 @@
     // it's job the two transforms cancel each other out and the compositor ends
     // up applying an identity transform to the app's buffer.
     err = native_window_set_buffers_transform(
-        surface.window.get(),
-        InvertTransformToNative(create_info->preTransform));
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+        window, InvertTransformToNative(create_info->preTransform));
+    if (err != android::OK) {
         ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
               InvertTransformToNative(create_info->preTransform),
               strerror(-err), err);
@@ -1162,10 +1179,8 @@
     }
 
     err = native_window_set_scaling_mode(
-        surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+        window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+    if (err != android::OK) {
         ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1175,28 +1190,25 @@
     if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
         create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
         swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID;
-        err = native_window_set_shared_buffer_mode(surface.window.get(), true);
-        if (err != 0) {
+        err = native_window_set_shared_buffer_mode(window, true);
+        if (err != android::OK) {
             ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
     }
 
     if (create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
-        err = native_window_set_auto_refresh(surface.window.get(), true);
-        if (err != 0) {
+        err = native_window_set_auto_refresh(window, true);
+        if (err != android::OK) {
             ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
             return VK_ERROR_SURFACE_LOST_KHR;
         }
     }
 
     int query_value;
-    err = surface.window->query(surface.window.get(),
-                                NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                                &query_value);
-    if (err != 0 || query_value < 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+                        &query_value);
+    if (err != android::OK || query_value < 0) {
         ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
               query_value);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1212,10 +1224,8 @@
     // in place for that to work yet. Note we only lie to the lower layer-- we
     // don't want to give the app back a swapchain with extra images (which they
     // can't actually use!).
-    err = native_window_set_buffer_count(surface.window.get(), std::max(2u, num_images));
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    err = native_window_set_buffer_count(window, std::max(2u, num_images));
+    if (err != android::OK) {
         ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
               strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
@@ -1224,7 +1234,7 @@
     int32_t legacy_usage = 0;
     if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
         uint64_t consumer_usage, producer_usage;
-        ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsage2ANDROID");
+        ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID");
         result = dispatch.GetSwapchainGrallocUsage2ANDROID(
             device, create_info->imageFormat, create_info->imageUsage,
             swapchain_image_usage, &consumer_usage, &producer_usage);
@@ -1236,7 +1246,7 @@
         legacy_usage =
             android_convertGralloc1To0Usage(producer_usage, consumer_usage);
     } else if (dispatch.GetSwapchainGrallocUsageANDROID) {
-        ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsageANDROID");
+        ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID");
         result = dispatch.GetSwapchainGrallocUsageANDROID(
             device, create_info->imageFormat, create_info->imageUsage,
             &legacy_usage);
@@ -1253,14 +1263,20 @@
         createProtectedSwapchain = true;
         native_usage |= BufferUsage::PROTECTED;
     }
-    err = native_window_set_usage(surface.window.get(), native_usage);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    err = native_window_set_usage(window, native_usage);
+    if (err != android::OK) {
         ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
 
+    int transform_hint;
+    err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
+    if (err != android::OK) {
+        ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
+              strerror(-err), err);
+        return VK_ERROR_SURFACE_LOST_KHR;
+    }
+
     // -- Allocate our Swapchain object --
     // After this point, we must deallocate the swapchain on error.
 
@@ -1311,11 +1327,8 @@
         Swapchain::Image& img = swapchain->images[i];
 
         ANativeWindowBuffer* buffer;
-        err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
-                                            &img.dequeue_fence);
-        if (err != 0) {
-            // TODO(jessehall): Improve error reporting. Can we enumerate
-            // possible errors and translate them to valid Vulkan result codes?
+        err = window->dequeueBuffer(window, &buffer, &img.dequeue_fence);
+        if (err != android::OK) {
             ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
             switch (-err) {
                 case ENOMEM:
@@ -1342,7 +1355,7 @@
             &image_native_buffer.usage2.producer,
             &image_native_buffer.usage2.consumer);
 
-        ATRACE_BEGIN("dispatch.CreateImage");
+        ATRACE_BEGIN("CreateImage");
         result =
             dispatch.CreateImage(device, &image_create, nullptr, &img.image);
         ATRACE_END();
@@ -1355,34 +1368,30 @@
     // -- Cancel all buffers, returning them to the queue --
     // If an error occurred before, also destroy the VkImage and release the
     // buffer reference. Otherwise, we retain a strong reference to the buffer.
-    //
-    // TODO(jessehall): The error path here is the same as DestroySwapchain,
-    // but not the non-error path. Should refactor/unify.
     for (uint32_t i = 0; i < num_images; i++) {
         Swapchain::Image& img = swapchain->images[i];
         if (img.dequeued) {
             if (!swapchain->shared) {
-                surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
-                                             img.dequeue_fence);
+                window->cancelBuffer(window, img.buffer.get(),
+                                     img.dequeue_fence);
                 img.dequeue_fence = -1;
                 img.dequeued = false;
             }
         }
-        if (result != VK_SUCCESS) {
-            if (img.image) {
-                ATRACE_BEGIN("dispatch.DestroyImage");
-                dispatch.DestroyImage(device, img.image, nullptr);
-                ATRACE_END();
-            }
-        }
     }
 
     if (result != VK_SUCCESS) {
-        swapchain->~Swapchain();
-        allocator->pfnFree(allocator->pUserData, swapchain);
+        DestroySwapchainInternal(device, HandleFromSwapchain(swapchain),
+                                 allocator);
         return result;
     }
 
+    if (transform_hint != swapchain->pre_transform) {
+        // Log that the app is not doing pre-rotation.
+        android::GraphicsEnv::getInstance().setTargetStats(
+            android::GpuStatsInfo::Stats::FALSE_PREROTATION);
+    }
+
     surface.swapchain_handle = HandleFromSwapchain(swapchain);
     *swapchain_handle = surface.swapchain_handle;
     return VK_SUCCESS;
@@ -1394,24 +1403,7 @@
                          const VkAllocationCallbacks* allocator) {
     ATRACE_CALL();
 
-    const auto& dispatch = GetData(device).driver;
-    Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
-    if (!swapchain)
-        return;
-    bool active = swapchain->surface.swapchain_handle == swapchain_handle;
-    ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr;
-
-    if (window && swapchain->frame_timestamps_enabled) {
-        native_window_enable_frame_timestamps(window, false);
-    }
-    for (uint32_t i = 0; i < swapchain->num_images; i++)
-        ReleaseSwapchainImage(device, window, -1, swapchain->images[i]);
-    if (active)
-        swapchain->surface.swapchain_handle = VK_NULL_HANDLE;
-    if (!allocator)
-        allocator = &GetData(device).allocator;
-    swapchain->~Swapchain();
-    allocator->pfnFree(allocator->pUserData, swapchain);
+    DestroySwapchainInternal(device, swapchain_handle, allocator);
 }
 
 VKAPI_ATTR
@@ -1459,10 +1451,6 @@
     if (swapchain.surface.swapchain_handle != swapchain_handle)
         return VK_ERROR_OUT_OF_DATE_KHR;
 
-    ALOGW_IF(
-        timeout != UINT64_MAX,
-        "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
-
     if (swapchain.shared) {
         // In shared mode, we keep the buffer dequeued all the time, so we don't
         // want to dequeue a buffer here. Instead, just ask the driver to ensure
@@ -1473,12 +1461,27 @@
         return result;
     }
 
+    const nsecs_t acquire_next_image_timeout =
+        timeout > (uint64_t)std::numeric_limits<nsecs_t>::max() ? -1 : timeout;
+    if (acquire_next_image_timeout != swapchain.acquire_next_image_timeout) {
+        // Cache the timeout to avoid the duplicate binder cost.
+        err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT,
+                              acquire_next_image_timeout);
+        if (err != android::OK) {
+            ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+                  strerror(-err), err);
+            return VK_ERROR_SURFACE_LOST_KHR;
+        }
+        swapchain.acquire_next_image_timeout = acquire_next_image_timeout;
+    }
+
     ANativeWindowBuffer* buffer;
     int fence_fd;
     err = window->dequeueBuffer(window, &buffer, &fence_fd);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
+    if (err == android::TIMED_OUT) {
+        ALOGW("dequeueBuffer timed out: %s (%d)", strerror(-err), err);
+        return timeout ? VK_TIMEOUT : VK_NOT_READY;
+    } else if (err != android::OK) {
         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
         return VK_ERROR_SURFACE_LOST_KHR;
     }
@@ -1533,8 +1536,6 @@
                               uint32_t* pImageIndex) {
     ATRACE_CALL();
 
-    // TODO: this should actually be the other way around and this function
-    // should handle any additional structures that get passed in
     return AcquireNextImageKHR(device, pAcquireInfo->swapchain,
                                pAcquireInfo->timeout, pAcquireInfo->semaphore,
                                pAcquireInfo->fence, pImageIndex);
@@ -1687,15 +1688,15 @@
                     uint64_t nativeFrameId = 0;
                     err = native_window_get_next_frame_id(
                             window, &nativeFrameId);
-                    if (err != android::NO_ERROR) {
+                    if (err != android::OK) {
                         ALOGE("Failed to get next native frame ID.");
                     }
 
                     // Add a new timing record with the user's presentID and
                     // the nativeFrameId.
-                    swapchain.timing.push_back(TimingInfo(time, nativeFrameId));
+                    swapchain.timing.emplace_back(time, nativeFrameId);
                     while (swapchain.timing.size() > MAX_TIMING_INFOS) {
-                        swapchain.timing.removeAt(0);
+                        swapchain.timing.erase(swapchain.timing.begin());
                     }
                     if (time->desiredPresentTime) {
                         // Set the desiredPresentTime:
@@ -1711,18 +1712,17 @@
 
                 err = window->queueBuffer(window, img.buffer.get(), fence);
                 // queueBuffer always closes fence, even on error
-                if (err != 0) {
-                    // TODO(jessehall): What now? We should probably cancel the
-                    // buffer, I guess?
+                if (err != android::OK) {
                     ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
                     swapchain_result = WorstPresentResult(
                         swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
+                } else {
+                    if (img.dequeue_fence >= 0) {
+                        close(img.dequeue_fence);
+                        img.dequeue_fence = -1;
+                    }
+                    img.dequeued = false;
                 }
-                if (img.dequeue_fence >= 0) {
-                    close(img.dequeue_fence);
-                    img.dequeue_fence = -1;
-                }
-                img.dequeued = false;
 
                 // If the swapchain is in shared mode, immediately dequeue the
                 // buffer so it can be presented again without an intervening
@@ -1732,30 +1732,27 @@
                     ANativeWindowBuffer* buffer;
                     int fence_fd;
                     err = window->dequeueBuffer(window, &buffer, &fence_fd);
-                    if (err != 0) {
+                    if (err != android::OK) {
                         ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
                         swapchain_result = WorstPresentResult(swapchain_result,
                             VK_ERROR_SURFACE_LOST_KHR);
-                    }
-                    else if (img.buffer != buffer) {
+                    } else if (img.buffer != buffer) {
                         ALOGE("got wrong image back for shared swapchain");
                         swapchain_result = WorstPresentResult(swapchain_result,
                             VK_ERROR_SURFACE_LOST_KHR);
-                    }
-                    else {
+                    } else {
                         img.dequeue_fence = fence_fd;
                         img.dequeued = true;
                     }
                 }
             }
             if (swapchain_result != VK_SUCCESS) {
-                ReleaseSwapchainImage(device, window, fence, img);
                 OrphanSwapchain(device, &swapchain);
             }
             int window_transform_hint;
             err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
                                 &window_transform_hint);
-            if (err != 0) {
+            if (err != android::OK) {
                 ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
                       strerror(-err), err);
                 swapchain_result = WorstPresentResult(
@@ -1807,6 +1804,10 @@
     ATRACE_CALL();
 
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    if (swapchain.surface.swapchain_handle != swapchain_handle) {
+        return VK_ERROR_OUT_OF_DATE_KHR;
+    }
+
     ANativeWindow* window = swapchain.surface.window.get();
     VkResult result = VK_SUCCESS;
 
@@ -1817,8 +1818,15 @@
     }
 
     if (timings) {
-        // TODO(ianelliott): plumb return value (e.g. VK_INCOMPLETE)
+        // Get the latest ready timing count before copying, since the copied
+        // timing info will be erased in copy_ready_timings function.
+        uint32_t n = get_num_ready_timings(swapchain);
         copy_ready_timings(swapchain, count, timings);
+        // Check the *count here against the recorded ready timing count, since
+        // *count can be overwritten per spec describes.
+        if (*count < n) {
+            result = VK_INCOMPLETE;
+        }
     } else {
         *count = get_num_ready_timings(swapchain);
     }
@@ -1839,7 +1847,7 @@
         return VK_ERROR_OUT_OF_DATE_KHR;
     }
 
-    // TODO(chrisforbes): Implement this function properly
+    // TODO(b/143296009): Implement this function properly
 
     return result;
 }
diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl
deleted file mode 100644
index ce15517..0000000
--- a/vulkan/nulldrv/null_driver.tmpl
+++ /dev/null
@@ -1,210 +0,0 @@
-{{/*
- * Copyright 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.
- */}}
-
-{{Include "../api/templates/vulkan_common.tmpl"}}
-{{Global "clang-format" (Strings "clang-format" "-style=file")}}
-{{Macro "DefineGlobals" $}}
-{{$ | Macro "null_driver_gen.h"   | Format (Global "clang-format") | Write "null_driver_gen.h"  }}
-{{$ | Macro "null_driver_gen.cpp" | Format (Global "clang-format") | Write "null_driver_gen.cpp"}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  null_driver_gen.h
--------------------------------------------------------------------------------
-*/}}
-{{define "null_driver_gen.h"}}
-/*
-•* Copyright 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.
-•*/

-// WARNING: This file is generated. See ../README.md for instructions.

-#ifndef NULLDRV_NULL_DRIVER_H
-#define NULLDRV_NULL_DRIVER_H 1

-#include <vulkan/vk_android_native_buffer.h>
-#include <vulkan/vulkan.h>

-namespace null_driver {«

-PFN_vkVoidFunction GetGlobalProcAddr(const char* name);
-PFN_vkVoidFunction GetInstanceProcAddr(const char* name);

-// clang-format off
-  {{range $f := AllCommands $}}
-    {{if (Macro "IsDriverFunction" $f)}}
-VKAPI_ATTR {{Node "Type" $f.Return}} {{Macro "BaseName" $f}}({{Macro "Parameters" $f}});
-    {{end}}
-  {{end}}
-VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
-VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
-VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
-// clang-format on

-»}  // namespace null_driver

-#endif  // NULLDRV_NULL_DRIVER_H
-¶{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  null_driver_gen.cpp
--------------------------------------------------------------------------------
-*/}}
-{{define "null_driver_gen.cpp"}}
-/*
-•* Copyright 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.
-•*/

-// WARNING: This file is generated. See ../README.md for instructions.

-#include "null_driver_gen.h"
-#include <algorithm>

-using namespace null_driver;

-namespace {

-struct NameProc {
-    const char* name;
-    PFN_vkVoidFunction proc;
-};

-PFN_vkVoidFunction Lookup(const char* name,
-                          const NameProc* begin,
-                          const NameProc* end) {
-    const auto& entry = std::lower_bound(
-        begin, end, name,
-        [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; });
-    if (entry == end || strcmp(entry->name, name) != 0)
-        return nullptr;
-    return entry->proc;
-}

-template <size_t N>
-PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) {
-    return Lookup(name, procs, procs + N);
-}

-const NameProc kGlobalProcs[] = {«
-  // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if and (Macro "IsDriverFunction" $f) (eq (Macro "Vtbl" $f) "Global")}}
-      {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
-        static_cast<{{Macro "FunctionPtrName" $f}}>(§
-          {{Macro "BaseName" $f}}))},
-    {{end}}
-  {{end}}
-  // clang-format on
-»};

-const NameProc kInstanceProcs[] = {«
-  // clang-format off
-  {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if (Macro "IsDriverFunction" $f)}}
-      {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§
-        static_cast<{{Macro "FunctionPtrName" $f}}>(§
-          {{Macro "BaseName" $f}}))},
-    {{end}}
-  {{end}}
-  // clang-format on
-»};

-} // namespace

-namespace null_driver {

-PFN_vkVoidFunction GetGlobalProcAddr(const char* name) {
-    return Lookup(name, kGlobalProcs);
-}

-PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {«
-    return Lookup(name, kInstanceProcs);
-»}

-} // namespace null_driver

-{{end}}
-
-
-{{/*
--------------------------------------------------------------------------------
-  Emits a function name without the "vk" prefix.
--------------------------------------------------------------------------------
-*/}}
-{{define "BaseName"}}
-  {{AssertType $ "Function"}}
-  {{TrimPrefix "vk" $.Name}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Emits 'true' if the API function is implemented by the driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsDriverFunction"}}
-  {{AssertType $ "Function"}}
-
-  {{if not (GetAnnotation $ "pfn")}}
-    {{$ext := GetAnnotation $ "extension"}}
-    {{if $ext}}
-      {{Macro "IsDriverExtension" $ext}}
-    {{else}}
-      true
-    {{end}}
-  {{end}}
-{{end}}
-
-
-{{/*
-------------------------------------------------------------------------------
-  Reports whether an extension is implemented by the driver.
-------------------------------------------------------------------------------
-*/}}
-{{define "IsDriverExtension"}}
-  {{$ext := index $.Arguments 0}}
-  {{     if eq $ext "VK_ANDROID_native_buffer"}}true
-  {{else if eq $ext "VK_EXT_debug_report"}}true
-  {{else if eq $ext "VK_KHR_get_physical_device_properties2"}}true
-  {{end}}
-{{end}}
diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp
index 92b7468..b8d7d2b 100644
--- a/vulkan/nulldrv/null_driver_gen.cpp
+++ b/vulkan/nulldrv/null_driver_gen.cpp
@@ -17,6 +17,7 @@
 // WARNING: This file is generated. See ../README.md for instructions.
 
 #include <algorithm>
+
 #include "null_driver_gen.h"
 
 using namespace null_driver;
diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h
index c6ad537..0d3f688 100644
--- a/vulkan/nulldrv/null_driver_gen.h
+++ b/vulkan/nulldrv/null_driver_gen.h
@@ -41,6 +41,7 @@
 VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties);
 VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
 VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion);
 VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties);
 VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties);
 VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties);
@@ -165,49 +166,45 @@
 VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents);
 VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer);
 VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers);
-VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion);
-VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos);
-VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
-VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures);
-VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask);
-VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
-VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
-VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
-VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
-VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
-VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures);
-VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties);
-VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties);
-VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties);
-VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
-VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
-VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
-VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
-VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
-VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion);
-VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
-VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
-VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
-VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
-VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties);
-VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
-VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
-VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int32_t* grallocUsage);
-VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
-VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
-VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 VKAPI_ATTR VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
 VKAPI_ATTR void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
 VKAPI_ATTR void DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage);
-VKAPI_ATTR void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures);
-VKAPI_ATTR void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties);
-VKAPI_ATTR void GetPhysicalDeviceFormatProperties2KHR(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties);
-VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties);
-VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties);
-VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties);
-VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures);
+VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceFormatProperties2KHR(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties);
+VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties);
+VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags);
+VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties);
+VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties);
+VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties);
+VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures);
+VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos);
+VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos);
+VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask);
+VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate);
+VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData);
+VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements);
+VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements);
+VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion);
+VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator);
+VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue);
+VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport);
 VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage);
+VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage);
 VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence);
 VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd);
 // clang-format on
diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py
new file mode 100644
index 0000000..7c39075
--- /dev/null
+++ b/vulkan/scripts/api_generator.py
@@ -0,0 +1,410 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Generates the api_gen.h and api_gen.cpp.
+"""
+
+import os
+import generator_common as gencom
+
+# Functions intercepted at vulkan::api level.
+_INTERCEPTED_COMMANDS = [
+    'vkCreateDevice',
+    'vkDestroyDevice',
+    'vkDestroyInstance',
+    'vkEnumerateDeviceExtensionProperties',
+    'vkEnumerateDeviceLayerProperties',
+]
+
+
+def gen_h():
+  """Generates the api_gen.h file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         '..', 'libvulkan', 'api_gen.h')
+
+  with open(genfile, 'w') as f:
+    instance_dispatch_table_entries = []
+    device_dispatch_table_entries = []
+
+    for cmd in gencom.command_list:
+      if cmd not in gencom.alias_dict:
+        if gencom.is_instance_dispatch_table_entry(cmd):
+          instance_dispatch_table_entries.append(
+              'PFN_' + cmd + ' ' + gencom.base_name(cmd) + ';')
+        elif gencom.is_device_dispatch_table_entry(cmd):
+          device_dispatch_table_entries.append(
+              'PFN_' + cmd + ' ' + gencom.base_name(cmd) + ';')
+
+    f.write(gencom.copyright_and_warning(2016))
+
+    f.write("""\
+#ifndef LIBVULKAN_API_GEN_H
+#define LIBVULKAN_API_GEN_H
+
+#include <vulkan/vulkan.h>
+
+#include <bitset>
+
+#include "driver_gen.h"
+
+namespace vulkan {
+namespace api {
+
+struct InstanceDispatchTable {
+    // clang-format off\n""")
+
+    for entry in instance_dispatch_table_entries:
+      f.write(gencom.indent(1) + entry + '\n')
+
+    f.write("""\
+    // clang-format on
+};
+
+struct DeviceDispatchTable {
+    // clang-format off\n""")
+
+    for entry in device_dispatch_table_entries:
+      f.write(gencom.indent(1) + entry + '\n')
+
+    f.write("""\
+    // clang-format on
+};
+
+bool InitDispatchTable(
+    VkInstance instance,
+    PFN_vkGetInstanceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions);
+
+}  // namespace api
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_API_GEN_H\n""")
+
+    f.close()
+  gencom.run_clang_format(genfile)
+
+
+def _define_extension_stub(cmd, f):
+  """Emits a stub for an exported extension function.
+
+  Args:
+    cmd: Vulkan function name.
+    f: Output file handle.
+  """
+  if (cmd in gencom.extension_dict and gencom.is_function_exported(cmd)):
+    ext_name = gencom.extension_dict[cmd]
+    ret = gencom.return_type_dict[cmd]
+    params = gencom.param_dict[cmd]
+    first_param = params[0][0] + params[0][1]
+    tail_params = ', '.join([i[0][:-1] for i in params[1:]])
+
+    f.write('VKAPI_ATTR ' + ret + ' disabled' + gencom.base_name(cmd) +
+            '(' + first_param + ', ' + tail_params + ') {\n')
+
+    f.write(gencom.indent(1) + 'driver::Logger(' + params[0][1] +
+            ').Err(' + params[0][1] + ', \"' + ext_name +
+            ' not enabled. Exported ' + cmd + ' not executed.\");\n')
+
+    if gencom.return_type_dict[cmd] != 'void':
+      f.write(gencom.indent(1) + 'return VK_SUCCESS;\n')
+
+    f.write('}\n\n')
+
+
+def _is_intercepted(cmd):
+  """Returns true if a function is intercepted by vulkan::api.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if gencom.is_function_supported(cmd):
+    if gencom.is_globally_dispatched(cmd) or cmd in _INTERCEPTED_COMMANDS:
+      return True
+  return False
+
+
+def _intercept_instance_proc_addr(f):
+  """Emits code for vkGetInstanceProcAddr for function interception.
+
+  Args:
+    f: Output file handle.
+  """
+  f.write("""\
+    // global functions
+    if (instance == VK_NULL_HANDLE) {\n""")
+
+  for cmd in gencom.command_list:
+    if gencom.is_globally_dispatched(cmd):
+      f.write(gencom.indent(2) +
+              'if (strcmp(pName, \"' + cmd +
+              '\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +
+              gencom.base_name(cmd) + ');\n')
+
+  f.write("""
+        ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \\\"%s\\\") call", pName);
+        return nullptr;
+    }
+
+    static const struct Hook {
+        const char* name;
+        PFN_vkVoidFunction proc;
+    } hooks[] = {\n""")
+
+  sorted_command_list = sorted(gencom.command_list)
+  for cmd in sorted_command_list:
+    if gencom.is_function_exported(cmd):
+      if gencom.is_globally_dispatched(cmd):
+        f.write(gencom.indent(2) + '{ \"' + cmd + '\", nullptr },\n')
+      elif (_is_intercepted(cmd) or
+            cmd == 'vkGetInstanceProcAddr' or
+            gencom.is_device_dispatched(cmd)):
+        f.write(gencom.indent(2) + '{ \"' + cmd +
+                '\", reinterpret_cast<PFN_vkVoidFunction>(' +
+                gencom.base_name(cmd) + ') },\n')
+
+  f.write("""\
+    };
+    // clang-format on
+    constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]);
+    auto hook = std::lower_bound(
+        hooks, hooks + count, pName,
+        [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; });
+    if (hook < hooks + count && strcmp(hook->name, pName) == 0) {
+        if (!hook->proc) {
+            vulkan::driver::Logger(instance).Err(
+                instance, "invalid vkGetInstanceProcAddr(%p, \\\"%s\\\") call",
+                instance, pName);
+        }
+        return hook->proc;
+    }
+    // clang-format off\n\n""")
+
+
+def _intercept_device_proc_addr(f):
+  """Emits code for vkGetDeviceProcAddr for function interception.
+
+  Args:
+    f: Output file handle.
+  """
+  f.write("""\
+    if (device == VK_NULL_HANDLE) {
+        ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call");
+        return nullptr;
+    }
+
+    static const char* const known_non_device_names[] = {\n""")
+
+  sorted_command_list = sorted(gencom.command_list)
+  for cmd in sorted_command_list:
+    if gencom.is_function_supported(cmd):
+      if not gencom.is_device_dispatched(cmd):
+        f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
+
+  f.write("""\
+    };
+    // clang-format on
+    constexpr size_t count =
+        sizeof(known_non_device_names) / sizeof(known_non_device_names[0]);
+    if (!pName ||
+        std::binary_search(
+            known_non_device_names, known_non_device_names + count, pName,
+            [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) {
+        vulkan::driver::Logger(device).Err(
+            device, "invalid vkGetDeviceProcAddr(%p, \\\"%s\\\") call", device,
+            (pName) ? pName : "(null)");
+        return nullptr;
+    }
+    // clang-format off\n\n""")
+
+  for cmd in gencom.command_list:
+    if gencom.is_device_dispatched(cmd):
+      if _is_intercepted(cmd) or cmd == 'vkGetDeviceProcAddr':
+        f.write(gencom.indent(1) + 'if (strcmp(pName, "' + cmd +
+                '") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' +
+                gencom.base_name(cmd) + ');\n')
+  f.write('\n')
+
+
+def _api_dispatch(cmd, f):
+  """Emits code to dispatch a function.
+
+  Args:
+    cmd: Vulkan function name.
+    f: Output file handle.
+  """
+  assert not _is_intercepted(cmd)
+
+  f.write(gencom.indent(1))
+  if gencom.return_type_dict[cmd] != 'void':
+    f.write('return ')
+
+  param_list = gencom.param_dict[cmd]
+  handle = param_list[0][1]
+  f.write('GetData(' + handle + ').dispatch.' + gencom.base_name(cmd) +
+          '(' + ', '.join(i[1] for i in param_list) + ');\n')
+
+
+def gen_cpp():
+  """Generates the api_gen.cpp file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         '..', 'libvulkan', 'api_gen.cpp')
+
+  with open(genfile, 'w') as f:
+    f.write(gencom.copyright_and_warning(2016))
+
+    f.write("""\
+#include <log/log.h>
+#include <string.h>
+
+#include <algorithm>
+
+// to catch mismatches between vulkan.h and this file
+#undef VK_NO_PROTOTYPES
+#include "api.h"
+
+namespace vulkan {
+namespace api {
+
+#define UNLIKELY(expr) __builtin_expect((expr), 0)
+
+#define INIT_PROC(required, obj, proc)                                 \\
+    do {                                                               \\
+        data.dispatch.proc =                                           \\
+            reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\
+        if (UNLIKELY(required && !data.dispatch.proc)) {               \\
+            ALOGE("missing " #obj " proc: vk" #proc);                  \\
+            success = false;                                           \\
+        }                                                              \\
+    } while (0)
+
+// Exported extension functions may be invoked even when their extensions
+// are disabled.  Dispatch to stubs when that happens.
+#define INIT_PROC_EXT(ext, required, obj, proc)  \\
+    do {                                         \\
+        if (extensions[driver::ProcHook::ext])   \\
+            INIT_PROC(required, obj, proc);      \\
+        else                                     \\
+            data.dispatch.proc = disabled##proc; \\
+    } while (0)
+
+namespace {
+
+// clang-format off\n\n""")
+
+    for cmd in gencom.command_list:
+      _define_extension_stub(cmd, f)
+
+    f.write("""\
+// clang-format on
+
+}  // namespace
+
+bool InitDispatchTable(
+    VkInstance instance,
+    PFN_vkGetInstanceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(instance);
+    bool success = true;
+
+    // clang-format off\n""")
+
+    for cmd in gencom.command_list:
+      if gencom.is_instance_dispatch_table_entry(cmd):
+        gencom.init_proc(cmd, f)
+
+    f.write("""\
+    // clang-format on
+
+    return success;
+}
+
+bool InitDispatchTable(
+    VkDevice dev,
+    PFN_vkGetDeviceProcAddr get_proc,
+    const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(dev);
+    bool success = true;
+
+    // clang-format off\n""")
+
+    for cmd in gencom.command_list:
+      if gencom.is_device_dispatch_table_entry(cmd):
+        gencom.init_proc(cmd, f)
+
+    f.write("""\
+    // clang-format on
+
+    return success;
+}
+
+// clang-format off
+
+namespace {
+
+// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr
+""")
+
+    for cmd in gencom.command_list:
+      if gencom.is_function_exported(cmd) and not _is_intercepted(cmd):
+        param_list = [''.join(i) for i in gencom.param_dict[cmd]]
+        f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' +
+                gencom.base_name(cmd) + '(' + ', '.join(param_list) + ');\n')
+
+    f.write('\n')
+    for cmd in gencom.command_list:
+      if gencom.is_function_exported(cmd) and not _is_intercepted(cmd):
+        param_list = [''.join(i) for i in gencom.param_dict[cmd]]
+        f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' +
+                gencom.base_name(cmd) + '(' + ', '.join(param_list) + ') {\n')
+        if cmd == 'vkGetInstanceProcAddr':
+          _intercept_instance_proc_addr(f)
+        elif cmd == 'vkGetDeviceProcAddr':
+          _intercept_device_proc_addr(f)
+        _api_dispatch(cmd, f)
+        f.write('}\n\n')
+
+    f.write("""
+}  // anonymous namespace
+
+// clang-format on
+
+}  // namespace api
+}  // namespace vulkan
+
+// clang-format off\n\n""")
+
+    for cmd in gencom.command_list:
+      if gencom.is_function_exported(cmd):
+        param_list = [''.join(i) for i in gencom.param_dict[cmd]]
+        f.write('__attribute__((visibility("default")))\n')
+        f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' +
+                cmd + '(' + ', '.join(param_list) + ') {\n')
+        f.write(gencom.indent(1))
+        if gencom.return_type_dict[cmd] != 'void':
+          f.write('return ')
+        param_list = gencom.param_dict[cmd]
+        f.write('vulkan::api::' + gencom.base_name(cmd) +
+                '(' + ', '.join(i[1] for i in param_list) + ');\n}\n\n')
+
+    f.write('// clang-format on\n')
+    f.close()
+  gencom.run_clang_format(genfile)
diff --git a/vulkan/scripts/code_generator.py b/vulkan/scripts/code_generator.py
new file mode 100755
index 0000000..2a017d2
--- /dev/null
+++ b/vulkan/scripts/code_generator.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Generates vulkan framework directly from the vulkan registry (vk.xml).
+"""
+
+import api_generator
+import driver_generator
+import generator_common
+import null_generator
+
+if __name__ == '__main__':
+  generator_common.parse_vulkan_registry()
+  api_generator.gen_h()
+  api_generator.gen_cpp()
+  driver_generator.gen_h()
+  driver_generator.gen_cpp()
+  null_generator.gen_h()
+  null_generator.gen_cpp()
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
new file mode 100644
index 0000000..a64a702
--- /dev/null
+++ b/vulkan/scripts/driver_generator.py
@@ -0,0 +1,546 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Generates the driver_gen.h and driver_gen.cpp.
+"""
+
+import os
+import generator_common as gencom
+
+# Extensions intercepted at vulkan::driver level.
+_INTERCEPTED_EXTENSIONS = [
+    'VK_ANDROID_native_buffer',
+    'VK_EXT_debug_report',
+    'VK_EXT_hdr_metadata',
+    'VK_EXT_swapchain_colorspace',
+    'VK_GOOGLE_display_timing',
+    'VK_KHR_android_surface',
+    'VK_KHR_get_surface_capabilities2',
+    'VK_KHR_incremental_present',
+    'VK_KHR_shared_presentable_image',
+    'VK_KHR_surface',
+    'VK_KHR_swapchain',
+]
+
+# Extensions known to vulkan::driver level.
+_KNOWN_EXTENSIONS = _INTERCEPTED_EXTENSIONS + [
+    'VK_ANDROID_external_memory_android_hardware_buffer',
+    'VK_KHR_bind_memory2',
+    'VK_KHR_get_physical_device_properties2',
+]
+
+# Functions needed at vulkan::driver level.
+_NEEDED_COMMANDS = [
+    # Create functions of dispatchable objects
+    'vkCreateDevice',
+    'vkGetDeviceQueue',
+    'vkGetDeviceQueue2',
+    'vkAllocateCommandBuffers',
+
+    # Destroy functions of dispatchable objects
+    'vkDestroyInstance',
+    'vkDestroyDevice',
+
+    # Enumeration of extensions
+    'vkEnumerateDeviceExtensionProperties',
+
+    # We cache physical devices in loader.cpp
+    'vkEnumeratePhysicalDevices',
+    'vkEnumeratePhysicalDeviceGroups',
+
+    'vkGetInstanceProcAddr',
+    'vkGetDeviceProcAddr',
+
+    'vkQueueSubmit',
+
+    # VK_KHR_swapchain->VK_ANDROID_native_buffer translation
+    'vkCreateImage',
+    'vkDestroyImage',
+
+    'vkGetPhysicalDeviceProperties',
+    'vkGetPhysicalDeviceProperties2',
+    'vkGetPhysicalDeviceProperties2KHR',
+
+    # VK_KHR_swapchain v69 requirement
+    'vkBindImageMemory2',
+    'vkBindImageMemory2KHR',
+]
+
+# Functions intercepted at vulkan::driver level.
+_INTERCEPTED_COMMANDS = [
+    # Create functions of dispatchable objects
+    'vkCreateInstance',
+    'vkCreateDevice',
+    'vkEnumeratePhysicalDevices',
+    'vkEnumeratePhysicalDeviceGroups',
+    'vkGetDeviceQueue',
+    'vkGetDeviceQueue2',
+    'vkAllocateCommandBuffers',
+
+    # Destroy functions of dispatchable objects
+    'vkDestroyInstance',
+    'vkDestroyDevice',
+
+    # Enumeration of extensions
+    'vkEnumerateInstanceExtensionProperties',
+    'vkEnumerateDeviceExtensionProperties',
+
+    'vkGetInstanceProcAddr',
+    'vkGetDeviceProcAddr',
+
+    'vkQueueSubmit',
+
+    # VK_KHR_swapchain v69 requirement
+    'vkBindImageMemory2',
+    'vkBindImageMemory2KHR',
+]
+
+
+def _is_driver_table_entry(cmd):
+  """Returns true if a function is needed by vulkan::driver.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if gencom.is_function_supported(cmd):
+    if cmd in _NEEDED_COMMANDS:
+      return True
+    if cmd in gencom.extension_dict:
+      if (gencom.extension_dict[cmd] == 'VK_ANDROID_native_buffer' or
+          gencom.extension_dict[cmd] == 'VK_EXT_debug_report'):
+        return True
+  return False
+
+
+def _is_instance_driver_table_entry(cmd):
+  """Returns true if a instance-dispatched function is needed by vulkan::driver.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  return (_is_driver_table_entry(cmd) and
+          gencom.is_instance_dispatched(cmd))
+
+
+def _is_device_driver_table_entry(cmd):
+  """Returns true if a device-dispatched function is needed by vulkan::driver.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  return (_is_driver_table_entry(cmd) and
+          gencom.is_device_dispatched(cmd))
+
+
+def gen_h():
+  """Generates the driver_gen.h file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         '..', 'libvulkan', 'driver_gen.h')
+
+  with open(genfile, 'w') as f:
+    f.write(gencom.copyright_and_warning(2016))
+
+    f.write("""\
+#ifndef LIBVULKAN_DRIVER_GEN_H
+#define LIBVULKAN_DRIVER_GEN_H
+
+#include <vulkan/vk_android_native_buffer.h>
+#include <vulkan/vulkan.h>
+
+#include <bitset>
+
+namespace vulkan {
+namespace driver {
+
+struct ProcHook {
+    enum Type {
+        GLOBAL,
+        INSTANCE,
+        DEVICE,
+    };
+    enum Extension {\n""")
+
+    for ext in _KNOWN_EXTENSIONS:
+      f.write(gencom.indent(2) + gencom.base_ext_name(ext) + ',\n')
+
+    f.write('\n')
+    for version in gencom.version_code_list:
+      f.write(gencom.indent(2) + 'EXTENSION_CORE_' + version + ',\n')
+
+    # EXTENSION_COUNT must be the next enum after the highest API version.
+    f.write("""\
+        EXTENSION_COUNT,
+        EXTENSION_UNKNOWN,
+    };
+
+    const char* name;
+    Type type;
+    Extension extension;
+
+    PFN_vkVoidFunction proc;
+    PFN_vkVoidFunction checked_proc;  // always nullptr for non-device hooks
+};
+
+struct InstanceDriverTable {
+    // clang-format off\n""")
+
+    for cmd in gencom.command_list:
+      if _is_instance_driver_table_entry(cmd):
+        f.write(gencom.indent(1) + 'PFN_' + cmd + ' ' +
+                gencom.base_name(cmd) + ';\n')
+
+    f.write("""\
+    // clang-format on
+};
+
+struct DeviceDriverTable {
+    // clang-format off\n""")
+
+    for cmd in gencom.command_list:
+      if _is_device_driver_table_entry(cmd):
+        f.write(gencom.indent(1) + 'PFN_' + cmd + ' ' +
+                gencom.base_name(cmd) + ';\n')
+
+    f.write("""\
+    // clang-format on
+};
+
+const ProcHook* GetProcHook(const char* name);
+ProcHook::Extension GetProcHookExtension(const char* name);
+
+bool InitDriverTable(VkInstance instance,
+                     PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+bool InitDriverTable(VkDevice dev,
+                     PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions);
+
+}  // namespace driver
+}  // namespace vulkan
+
+#endif  // LIBVULKAN_DRIVER_TABLE_H\n""")
+
+    f.close()
+  gencom.run_clang_format(genfile)
+
+
+def _is_intercepted(cmd):
+  """Returns true if a function is intercepted by vulkan::driver.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if gencom.is_function_supported(cmd):
+    if cmd in _INTERCEPTED_COMMANDS:
+      return True
+
+    if cmd in gencom.extension_dict:
+      return gencom.extension_dict[cmd] in _INTERCEPTED_EXTENSIONS
+  return False
+
+
+def _get_proc_hook_enum(cmd):
+  """Returns the ProcHook enumeration for the corresponding core function.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  assert cmd in gencom.version_dict
+  for version in gencom.version_code_list:
+    if gencom.version_dict[cmd] == 'VK_VERSION_' + version:
+      return 'ProcHook::EXTENSION_CORE_' + version
+
+
+def _need_proc_hook_stub(cmd):
+  """Returns true if a function needs a ProcHook stub.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if _is_intercepted(cmd) and gencom.is_device_dispatched(cmd):
+    if cmd in gencom.extension_dict:
+      if not gencom.is_extension_internal(gencom.extension_dict[cmd]):
+        return True
+    elif gencom.version_dict[cmd] != 'VK_VERSION_1_0':
+      return True
+  return False
+
+
+def _define_proc_hook_stub(cmd, f):
+  """Emits a stub for ProcHook::checked_proc.
+
+  Args:
+    cmd: Vulkan function name.
+    f: Output file handle.
+  """
+  if _need_proc_hook_stub(cmd):
+    return_type = gencom.return_type_dict[cmd]
+
+    ext_name = ''
+    ext_hook = ''
+    if cmd in gencom.extension_dict:
+      ext_name = gencom.extension_dict[cmd]
+      ext_hook = 'ProcHook::' + gencom.base_ext_name(ext_name)
+    else:
+      ext_name = gencom.version_dict[cmd]
+      ext_hook = _get_proc_hook_enum(cmd)
+
+    handle = gencom.param_dict[cmd][0][1]
+    param_types = ', '.join([''.join(i) for i in gencom.param_dict[cmd]])
+    param_names = ', '.join([''.join(i[1]) for i in gencom.param_dict[cmd]])
+
+    f.write('VKAPI_ATTR ' + return_type + ' checked' + gencom.base_name(cmd) +
+            '(' + param_types + ') {\n')
+    f.write(gencom.indent(1) + 'if (GetData(' + handle + ').hook_extensions[' +
+            ext_hook + ']) {\n')
+
+    f.write(gencom.indent(2))
+    if gencom.return_type_dict[cmd] != 'void':
+      f.write('return ')
+    f.write(gencom.base_name(cmd) + '(' + param_names + ');\n')
+
+    f.write(gencom.indent(1) + '} else {\n')
+    f.write(gencom.indent(2) + 'Logger(' + handle + ').Err(' + handle + ', \"' +
+            ext_name + ' not enabled. ' + cmd + ' not executed.\");\n')
+    if gencom.return_type_dict[cmd] != 'void':
+      f.write(gencom.indent(2) + 'return VK_SUCCESS;\n')
+    f.write(gencom.indent(1) + '}\n}\n\n')
+
+
+def _define_global_proc_hook(cmd, f):
+  """Emits definition of a global ProcHook.
+
+  Args:
+    cmd: Vulkan function name.
+    f: Output file handle.
+  """
+  assert cmd not in gencom.extension_dict
+
+  f.write(gencom.indent(1) + '{\n')
+  f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
+  f.write(gencom.indent(2) + 'ProcHook::GLOBAL,\n')
+  f.write(gencom.indent(2) + _get_proc_hook_enum(cmd) + ',\n')
+  f.write(gencom.indent(2) + 'reinterpret_cast<PFN_vkVoidFunction>(' +
+          gencom.base_name(cmd) + '),\n')
+  f.write(gencom.indent(2) + 'nullptr,\n')
+  f.write(gencom.indent(1) + '},\n')
+
+
+def _define_instance_proc_hook(cmd, f):
+  """Emits definition of a instance ProcHook.
+
+  Args:
+    cmd: Vulkan function name.
+    f: Output file handle.
+  """
+  f.write(gencom.indent(1) + '{\n')
+  f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
+  f.write(gencom.indent(2) + 'ProcHook::INSTANCE,\n')
+
+  if cmd in gencom.extension_dict:
+    ext_name = gencom.extension_dict[cmd]
+    f.write(gencom.indent(2) + 'ProcHook::' +
+            gencom.base_ext_name(ext_name) + ',\n')
+
+    if gencom.is_extension_internal(ext_name):
+      f.write("""\
+        nullptr,
+        nullptr,\n""")
+    else:
+      f.write("""\
+        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
+        nullptr,\n""")
+  else:
+    f.write(gencom.indent(2) + _get_proc_hook_enum(cmd) + ',\n')
+    f.write("""\
+        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
+        nullptr,\n""")
+
+  f.write(gencom.indent(1) + '},\n')
+
+
+def _define_device_proc_hook(cmd, f):
+  """Emits definition of a device ProcHook.
+
+  Args:
+    cmd: Vulkan function name.
+    f: Output file handle.
+  """
+  f.write(gencom.indent(1) + '{\n')
+  f.write(gencom.indent(2) + '\"' + cmd + '\",\n')
+  f.write(gencom.indent(2) + 'ProcHook::DEVICE,\n')
+
+  if (cmd in gencom.extension_dict or
+      gencom.version_dict[cmd] != 'VK_VERSION_1_0'):
+    ext_name = ''
+    ext_hook = ''
+    if cmd in gencom.extension_dict:
+      ext_name = gencom.extension_dict[cmd]
+      ext_hook = 'ProcHook::' + gencom.base_ext_name(ext_name)
+    else:
+      ext_name = gencom.version_dict[cmd]
+      ext_hook = _get_proc_hook_enum(cmd)
+    f.write(gencom.indent(2) + ext_hook + ',\n')
+
+    if gencom.is_extension_internal(ext_name):
+      f.write("""\
+        nullptr,
+        nullptr,\n""")
+    else:
+      f.write("""\
+        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
+        reinterpret_cast<PFN_vkVoidFunction>(checked""" +
+              gencom.base_name(cmd) + '),\n')
+
+  else:
+    f.write(gencom.indent(2) + _get_proc_hook_enum(cmd) + ',\n')
+    f.write("""\
+        reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """),
+        nullptr,\n""")
+
+  f.write(gencom.indent(1) + '},\n')
+
+
+def gen_cpp():
+  """Generates the driver_gen.cpp file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         '..', 'libvulkan', 'driver_gen.cpp')
+
+  with open(genfile, 'w') as f:
+    f.write(gencom.copyright_and_warning(2016))
+    f.write("""\
+#include <log/log.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "driver.h"
+
+namespace vulkan {
+namespace driver {
+
+namespace {
+
+// clang-format off\n\n""")
+
+    for cmd in gencom.command_list:
+      _define_proc_hook_stub(cmd, f)
+
+    f.write("""\
+// clang-format on
+
+const ProcHook g_proc_hooks[] = {
+    // clang-format off\n""")
+
+    sorted_command_list = sorted(gencom.command_list)
+    for cmd in sorted_command_list:
+      if _is_intercepted(cmd):
+        if gencom.is_globally_dispatched(cmd):
+          _define_global_proc_hook(cmd, f)
+        elif gencom.is_instance_dispatched(cmd):
+          _define_instance_proc_hook(cmd, f)
+        elif gencom.is_device_dispatched(cmd):
+          _define_device_proc_hook(cmd, f)
+
+    f.write("""\
+    // clang-format on
+};
+
+}  // namespace
+
+const ProcHook* GetProcHook(const char* name) {
+    const auto& begin = g_proc_hooks;
+    const auto& end =
+        g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]);
+    const auto hook = std::lower_bound(
+        begin, end, name,
+        [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; });
+    return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr;
+}
+
+ProcHook::Extension GetProcHookExtension(const char* name) {
+    // clang-format off\n""")
+
+    for ext in _KNOWN_EXTENSIONS:
+      f.write(gencom.indent(1) + 'if (strcmp(name, \"' + ext +
+              '\") == 0) return ProcHook::' + gencom.base_ext_name(ext) + ';\n')
+
+    f.write("""\
+    // clang-format on
+    return ProcHook::EXTENSION_UNKNOWN;
+}
+
+#define UNLIKELY(expr) __builtin_expect((expr), 0)
+
+#define INIT_PROC(required, obj, proc)                                 \\
+    do {                                                               \\
+        data.driver.proc =                                             \\
+            reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\
+        if (UNLIKELY(required && !data.driver.proc)) {                 \\
+            ALOGE("missing " #obj " proc: vk" #proc);                  \\
+            success = false;                                           \\
+        }                                                              \\
+    } while (0)
+
+#define INIT_PROC_EXT(ext, required, obj, proc) \\
+    do {                                        \\
+        if (extensions[ProcHook::ext])          \\
+            INIT_PROC(required, obj, proc);     \\
+    } while (0)
+
+bool InitDriverTable(VkInstance instance,
+                     PFN_vkGetInstanceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(instance);
+    bool success = true;
+
+    // clang-format off\n""")
+
+    for cmd in gencom.command_list:
+      if _is_instance_driver_table_entry(cmd):
+        gencom.init_proc(cmd, f)
+
+    f.write("""\
+    // clang-format on
+
+    return success;
+}
+
+bool InitDriverTable(VkDevice dev,
+                     PFN_vkGetDeviceProcAddr get_proc,
+                     const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) {
+    auto& data = GetData(dev);
+    bool success = true;
+
+    // clang-format off\n""")
+
+    for cmd in gencom.command_list:
+      if _is_device_driver_table_entry(cmd):
+        gencom.init_proc(cmd, f)
+
+    f.write("""\
+    // clang-format on
+
+    return success;
+}
+
+}  // namespace driver
+}  // namespace vulkan\n""")
+
+    f.close()
+  gencom.run_clang_format(genfile)
diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py
new file mode 100644
index 0000000..cf370fa
--- /dev/null
+++ b/vulkan/scripts/generator_common.py
@@ -0,0 +1,406 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Provide the utilities for framework generation.
+"""
+
+import os
+import subprocess
+import xml.etree.ElementTree as element_tree
+
+# Extensions unsupported on Android.
+_BLACKLISTED_EXTENSIONS = [
+    'VK_EXT_acquire_xlib_display',
+    'VK_EXT_direct_mode_display',
+    'VK_EXT_display_control',
+    'VK_EXT_display_surface_counter',
+    'VK_EXT_full_screen_exclusive',
+    'VK_EXT_headless_surface',
+    'VK_EXT_metal_surface',
+    'VK_FUCHSIA_imagepipe_surface',
+    'VK_GGP_stream_descriptor_surface',
+    'VK_KHR_display',
+    'VK_KHR_display_swapchain',
+    'VK_KHR_external_fence_win32',
+    'VK_KHR_external_memory_win32',
+    'VK_KHR_external_semaphore_win32',
+    'VK_KHR_mir_surface',
+    'VK_KHR_wayland_surface',
+    'VK_KHR_win32_keyed_mutex',
+    'VK_KHR_win32_surface',
+    'VK_KHR_xcb_surface',
+    'VK_KHR_xlib_surface',
+    'VK_MVK_ios_surface',
+    'VK_MVK_macos_surface',
+    'VK_NN_vi_surface',
+    'VK_NV_cooperative_matrix',
+    'VK_NV_coverage_reduction_mode',
+    'VK_NV_external_memory_win32',
+    'VK_NV_win32_keyed_mutex',
+    'VK_NVX_image_view_handle',
+]
+
+# Extensions having functions exported by the loader.
+_EXPORTED_EXTENSIONS = [
+    'VK_ANDROID_external_memory_android_hardware_buffer',
+    'VK_KHR_android_surface',
+    'VK_KHR_surface',
+    'VK_KHR_swapchain',
+]
+
+# Functions optional on Android even if extension is advertised.
+_OPTIONAL_COMMANDS = [
+    'vkGetSwapchainGrallocUsageANDROID',
+    'vkGetSwapchainGrallocUsage2ANDROID',
+]
+
+# Dict for mapping dispatch table to a type.
+_DISPATCH_TYPE_DICT = {
+    'VkInstance ': 'Instance',
+    'VkPhysicalDevice ': 'Instance',
+    'VkDevice ': 'Device',
+    'VkQueue ': 'Device',
+    'VkCommandBuffer ': 'Device'
+}
+
+# Dict for mapping a function to its alias.
+alias_dict = {}
+
+# List of all the Vulkan functions.
+command_list = []
+
+# Dict for mapping a function to an extension.
+extension_dict = {}
+
+# Dict for mapping a function to all its parameters.
+param_dict = {}
+
+# Dict for mapping a function to its return type.
+return_type_dict = {}
+
+# List of the sorted Vulkan version codes. e.g. '1_0', '1_1'.
+version_code_list = []
+
+# Dict for mapping a function to the core Vulkan API version.
+version_dict = {}
+
+
+def indent(num):
+  """Returns the requested indents.
+
+  Args:
+    num: Number of the 4-space indents.
+  """
+  return '    ' * num
+
+
+def copyright_and_warning(year):
+  """Returns the standard copyright and warning codes.
+
+  Args:
+    year: An integer year for the copyright.
+  """
+  return """\
+/*
+ * Copyright """ + str(year) + """ The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// WARNING: This file is generated. See ../README.md for instructions.
+
+"""
+
+
+def run_clang_format(args):
+  """Run clang format on the file.
+
+  Args:
+    args: The file to be formatted.
+  """
+  clang_call = ['clang-format', '--style', 'file', '-i', args]
+  subprocess.check_call(clang_call)
+
+
+def is_extension_internal(ext):
+  """Returns true if an extension is internal to the loader and drivers.
+
+  The loader should not enumerate this extension.
+
+  Args:
+    ext: Vulkan extension name.
+  """
+  return ext == 'VK_ANDROID_native_buffer'
+
+
+def base_name(cmd):
+  """Returns a function name without the 'vk' prefix.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  return cmd[2:]
+
+
+def base_ext_name(ext):
+  """Returns an extension name without the 'VK_' prefix.
+
+  Args:
+    ext: Vulkan extension name.
+  """
+  return ext[3:]
+
+
+def version_code(version):
+  """Returns the version code from a version string.
+
+  Args:
+    version: Vulkan version string.
+  """
+  return version[11:]
+
+
+def is_function_supported(cmd):
+  """Returns true if a function is core or from a supportable extension.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if cmd not in extension_dict:
+    return True
+  else:
+    if extension_dict[cmd] not in _BLACKLISTED_EXTENSIONS:
+      return True
+  return False
+
+
+def get_dispatch_table_type(cmd):
+  """Returns the dispatch table type for a function.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if cmd not in param_dict:
+    return None
+
+  if param_dict[cmd]:
+    return _DISPATCH_TYPE_DICT.get(param_dict[cmd][0][0], 'Global')
+  return 'Global'
+
+
+def is_globally_dispatched(cmd):
+  """Returns true if the function is global, which is not dispatched.
+
+  Only global functions and functions handled in the loader top without calling
+  into lower layers are not dispatched.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  return is_function_supported(cmd) and get_dispatch_table_type(cmd) == 'Global'
+
+
+def is_instance_dispatched(cmd):
+  """Returns true for functions that can have instance-specific dispatch.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  return (is_function_supported(cmd) and
+          get_dispatch_table_type(cmd) == 'Instance')
+
+
+def is_device_dispatched(cmd):
+  """Returns true for functions that can have device-specific dispatch.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  return is_function_supported(cmd) and get_dispatch_table_type(cmd) == 'Device'
+
+
+def is_extension_exported(ext):
+  """Returns true if an extension has functions exported by the loader.
+
+  E.g. applications can directly link to an extension function.
+
+  Args:
+    ext: Vulkan extension name.
+  """
+  return ext in _EXPORTED_EXTENSIONS
+
+
+def is_function_exported(cmd):
+  """Returns true if a function is exported from the Android Vulkan library.
+
+  Functions in the core API and in loader extensions are exported.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if is_function_supported(cmd):
+    if cmd in extension_dict:
+      return is_extension_exported(extension_dict[cmd])
+    return True
+  return False
+
+
+def is_instance_dispatch_table_entry(cmd):
+  """Returns true if a function is exported and instance-dispatched.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if cmd == 'vkEnumerateDeviceLayerProperties':
+    # deprecated, unused internally - @dbd33bc
+    return False
+  return is_function_exported(cmd) and is_instance_dispatched(cmd)
+
+
+def is_device_dispatch_table_entry(cmd):
+  """Returns true if a function is exported and device-dispatched.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  return is_function_exported(cmd) and is_device_dispatched(cmd)
+
+
+def init_proc(name, f):
+  """Emits code to invoke INIT_PROC or INIT_PROC_EXT.
+
+  Args:
+    name: Vulkan function name.
+    f: Output file handle.
+  """
+  f.write(indent(1))
+  if name in extension_dict:
+    f.write('INIT_PROC_EXT(' + base_ext_name(extension_dict[name]) + ', ')
+  else:
+    f.write('INIT_PROC(')
+
+  if name in version_dict and version_dict[name] == 'VK_VERSION_1_1':
+    f.write('false, ')
+  elif name in _OPTIONAL_COMMANDS:
+    f.write('false, ')
+  else:
+    f.write('true, ')
+
+  if is_instance_dispatched(name):
+    f.write('instance, ')
+  else:
+    f.write('dev, ')
+
+  f.write(base_name(name) + ');\n')
+
+
+def parse_vulkan_registry():
+  """Parses Vulkan registry into the below global variables.
+
+  alias_dict
+  command_list
+  extension_dict
+  param_dict
+  return_type_dict
+  version_code_list
+  version_dict
+  """
+  registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..',
+                          'external', 'vulkan-headers', 'registry', 'vk.xml')
+  tree = element_tree.parse(registry)
+  root = tree.getroot()
+  for commands in root.iter('commands'):
+    for command in commands:
+      if command.tag == 'command':
+        parameter_list = []
+        protoset = False
+        cmd_name = ''
+        cmd_type = ''
+        if command.get('alias') is not None:
+          alias = command.get('alias')
+          cmd_name = command.get('name')
+          alias_dict[cmd_name] = alias
+          command_list.append(cmd_name)
+          param_dict[cmd_name] = param_dict[alias].copy()
+          return_type_dict[cmd_name] = return_type_dict[alias]
+        for params in command:
+          if params.tag == 'param':
+            param_type = ''
+            if params.text is not None and params.text.strip():
+              param_type = params.text.strip() + ' '
+            type_val = params.find('type')
+            param_type = param_type + type_val.text
+            if type_val.tail is not None:
+              param_type += type_val.tail.strip() + ' '
+            pname = params.find('name')
+            param_name = pname.text
+            if pname.tail is not None and pname.tail.strip():
+              parameter_list.append(
+                  (param_type, param_name, pname.tail.strip()))
+            else:
+              parameter_list.append((param_type, param_name))
+          if params.tag == 'proto':
+            for c in params:
+              if c.tag == 'type':
+                cmd_type = c.text
+              if c.tag == 'name':
+                cmd_name = c.text
+                protoset = True
+                command_list.append(cmd_name)
+                return_type_dict[cmd_name] = cmd_type
+        if protoset:
+          param_dict[cmd_name] = parameter_list.copy()
+
+  for exts in root.iter('extensions'):
+    for extension in exts:
+      apiversion = ''
+      if extension.tag == 'extension':
+        extname = extension.get('name')
+        for req in extension:
+          if req.get('feature') is not None:
+            apiversion = req.get('feature')
+          for commands in req:
+            if commands.tag == 'command':
+              cmd_name = commands.get('name')
+              if cmd_name not in extension_dict:
+                extension_dict[cmd_name] = extname
+                if apiversion:
+                  version_dict[cmd_name] = apiversion
+
+  for feature in root.iter('feature'):
+    apiversion = feature.get('name')
+    for req in feature:
+      for command in req:
+        if command.tag == 'command':
+          cmd_name = command.get('name')
+          if cmd_name in command_list:
+            version_dict[cmd_name] = apiversion
+
+  version_code_set = set()
+  for version in version_dict.values():
+    version_code_set.add(version_code(version))
+  for code in sorted(version_code_set):
+    version_code_list.append(code)
diff --git a/vulkan/scripts/null_generator.py b/vulkan/scripts/null_generator.py
new file mode 100644
index 0000000..e9faef6
--- /dev/null
+++ b/vulkan/scripts/null_generator.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Generates the null_driver_gen.h and null_driver_gen.cpp.
+"""
+
+import os
+import generator_common as gencom
+
+# Extensions implemented by the driver.
+_DRIVER_EXTENSION_DICT = {
+    'VK_ANDROID_native_buffer',
+    'VK_EXT_debug_report',
+    'VK_KHR_get_physical_device_properties2'
+}
+
+
+def _is_driver_function(cmd):
+  """Returns true if the function is implemented by the driver.
+
+  Args:
+    cmd: Vulkan function name.
+  """
+  if cmd in gencom.extension_dict:
+    return gencom.extension_dict[cmd] in _DRIVER_EXTENSION_DICT
+  return True
+
+
+def gen_h():
+  """Generates the null_driver_gen.h file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         '..', 'nulldrv', 'null_driver_gen.h')
+
+  with open(genfile, 'w') as f:
+    f.write(gencom.copyright_and_warning(2015))
+
+    f.write("""\
+#ifndef NULLDRV_NULL_DRIVER_H
+#define NULLDRV_NULL_DRIVER_H 1
+
+#include <vulkan/vk_android_native_buffer.h>
+#include <vulkan/vulkan.h>
+
+namespace null_driver {
+
+PFN_vkVoidFunction GetGlobalProcAddr(const char* name);
+PFN_vkVoidFunction GetInstanceProcAddr(const char* name);
+
+// clang-format off\n""")
+
+    for cmd in gencom.command_list:
+      if _is_driver_function(cmd):
+        param_list = [''.join(i) for i in gencom.param_dict[cmd]]
+        f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' +
+                gencom.base_name(cmd) + '(' + ', '.join(param_list) + ');\n')
+
+    f.write("""\
+// clang-format on
+
+}  // namespace null_driver
+
+#endif  // NULLDRV_NULL_DRIVER_H\n""")
+
+    f.close()
+  gencom.run_clang_format(genfile)
+
+
+def gen_cpp():
+  """Generates the null_driver_gen.cpp file.
+  """
+  genfile = os.path.join(os.path.dirname(__file__),
+                         '..', 'nulldrv', 'null_driver_gen.cpp')
+
+  with open(genfile, 'w') as f:
+    f.write(gencom.copyright_and_warning(2015))
+
+    f.write("""\
+#include <algorithm>
+
+#include "null_driver_gen.h"
+
+using namespace null_driver;
+
+namespace {
+
+struct NameProc {
+    const char* name;
+    PFN_vkVoidFunction proc;
+};
+
+PFN_vkVoidFunction Lookup(const char* name,
+                          const NameProc* begin,
+                          const NameProc* end) {
+    const auto& entry = std::lower_bound(
+        begin, end, name,
+        [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; });
+    if (entry == end || strcmp(entry->name, name) != 0)
+        return nullptr;
+    return entry->proc;
+}
+
+template <size_t N>
+PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) {
+    return Lookup(name, procs, procs + N);
+}
+
+const NameProc kGlobalProcs[] = {
+    // clang-format off\n""")
+
+    sorted_command_list = sorted(gencom.command_list)
+    for cmd in sorted_command_list:
+      if (_is_driver_function(cmd) and
+          gencom.get_dispatch_table_type(cmd) == 'Global'):
+        f.write(gencom.indent(1) + '{\"' + cmd +
+                '\", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_' +
+                cmd + '>(' + gencom.base_name(cmd) + '))},\n')
+
+    f.write("""\
+    // clang-format on
+};
+
+const NameProc kInstanceProcs[] = {
+    // clang-format off\n""")
+
+    for cmd in sorted_command_list:
+      if _is_driver_function(cmd):
+        f.write(gencom.indent(1) + '{\"' + cmd +
+                '\", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_' +
+                cmd + '>(' + gencom.base_name(cmd) + '))},\n')
+
+    f.write("""\
+    // clang-format on
+};
+
+}  // namespace
+
+namespace null_driver {
+
+PFN_vkVoidFunction GetGlobalProcAddr(const char* name) {
+    return Lookup(name, kGlobalProcs);
+}
+
+PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {
+    return Lookup(name, kInstanceProcs);
+}
+
+}  // namespace null_driver\n""")
+
+    f.close()
+  gencom.run_clang_format(genfile)
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
deleted file mode 100644
index f9e4916..0000000
--- a/vulkan/tools/vkinfo.cpp
+++ /dev/null
@@ -1,634 +0,0 @@
-/*
- * Copyright 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.
- */
-
-#include <inttypes.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <array>
-#include <sstream>
-#include <vector>
-
-#include <vulkan/vulkan.h>
-
-namespace {
-
-struct Options {
-    bool layer_description;
-    bool layer_extensions;
-    bool unsupported_features;
-    bool validate;
-};
-
-struct GpuInfo {
-    VkPhysicalDeviceProperties properties;
-    VkPhysicalDeviceMemoryProperties memory;
-    VkPhysicalDeviceFeatures features;
-    std::vector<VkQueueFamilyProperties> queue_families;
-    std::vector<VkExtensionProperties> extensions;
-    std::vector<VkLayerProperties> layers;
-    std::vector<std::vector<VkExtensionProperties>> layer_extensions;
-};
-struct VulkanInfo {
-    std::vector<VkExtensionProperties> extensions;
-    std::vector<VkLayerProperties> layers;
-    std::vector<std::vector<VkExtensionProperties>> layer_extensions;
-    std::vector<GpuInfo> gpus;
-};
-
-// ----------------------------------------------------------------------------
-
-[[noreturn]] void die(const char* proc, VkResult result) {
-    const char* result_str;
-    switch (result) {
-        // clang-format off
-        case VK_SUCCESS: result_str = "VK_SUCCESS"; break;
-        case VK_NOT_READY: result_str = "VK_NOT_READY"; break;
-        case VK_TIMEOUT: result_str = "VK_TIMEOUT"; break;
-        case VK_EVENT_SET: result_str = "VK_EVENT_SET"; break;
-        case VK_EVENT_RESET: result_str = "VK_EVENT_RESET"; break;
-        case VK_INCOMPLETE: result_str = "VK_INCOMPLETE"; break;
-        case VK_ERROR_OUT_OF_HOST_MEMORY: result_str = "VK_ERROR_OUT_OF_HOST_MEMORY"; break;
-        case VK_ERROR_OUT_OF_DEVICE_MEMORY: result_str = "VK_ERROR_OUT_OF_DEVICE_MEMORY"; break;
-        case VK_ERROR_INITIALIZATION_FAILED: result_str = "VK_ERROR_INITIALIZATION_FAILED"; break;
-        case VK_ERROR_DEVICE_LOST: result_str = "VK_ERROR_DEVICE_LOST"; break;
-        case VK_ERROR_MEMORY_MAP_FAILED: result_str = "VK_ERROR_MEMORY_MAP_FAILED"; break;
-        case VK_ERROR_LAYER_NOT_PRESENT: result_str = "VK_ERROR_LAYER_NOT_PRESENT"; break;
-        case VK_ERROR_EXTENSION_NOT_PRESENT: result_str = "VK_ERROR_EXTENSION_NOT_PRESENT"; break;
-        case VK_ERROR_INCOMPATIBLE_DRIVER: result_str = "VK_ERROR_INCOMPATIBLE_DRIVER"; break;
-        default: result_str = "<unknown VkResult>"; break;
-            // clang-format on
-    }
-    fprintf(stderr, "%s failed: %s (%d)\n", proc, result_str, result);
-    exit(1);
-}
-
-bool HasExtension(const std::vector<VkExtensionProperties>& extensions,
-                  const char* name) {
-    return std::find_if(extensions.cbegin(), extensions.cend(),
-                        [=](const VkExtensionProperties& prop) {
-                            return strcmp(prop.extensionName, name) == 0;
-                        }) != extensions.end();
-}
-
-void EnumerateInstanceExtensions(
-    const char* layer_name,
-    std::vector<VkExtensionProperties>* extensions) {
-    VkResult result;
-    uint32_t count;
-    result =
-        vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceExtensionProperties (count)", result);
-    do {
-        extensions->resize(count);
-        result = vkEnumerateInstanceExtensionProperties(layer_name, &count,
-                                                        extensions->data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceExtensionProperties (data)", result);
-}
-
-void EnumerateDeviceExtensions(VkPhysicalDevice gpu,
-                               const char* layer_name,
-                               std::vector<VkExtensionProperties>* extensions) {
-    VkResult result;
-    uint32_t count;
-    result =
-        vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceExtensionProperties (count)", result);
-    do {
-        extensions->resize(count);
-        result = vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count,
-                                                      extensions->data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceExtensionProperties (data)", result);
-}
-
-void GatherGpuInfo(VkPhysicalDevice gpu,
-                   const Options &options,
-                   GpuInfo& info) {
-    VkResult result;
-    uint32_t count;
-
-    vkGetPhysicalDeviceProperties(gpu, &info.properties);
-    vkGetPhysicalDeviceMemoryProperties(gpu, &info.memory);
-    vkGetPhysicalDeviceFeatures(gpu, &info.features);
-
-    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, nullptr);
-    info.queue_families.resize(count);
-    vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count,
-                                             info.queue_families.data());
-
-    result = vkEnumerateDeviceLayerProperties(gpu, &count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceLayerProperties (count)", result);
-    do {
-        info.layers.resize(count);
-        result =
-            vkEnumerateDeviceLayerProperties(gpu, &count, info.layers.data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateDeviceLayerProperties (data)", result);
-    info.layer_extensions.resize(info.layers.size());
-
-    EnumerateDeviceExtensions(gpu, nullptr, &info.extensions);
-    for (size_t i = 0; i < info.layers.size(); i++) {
-        EnumerateDeviceExtensions(gpu, info.layers[i].layerName,
-                                  &info.layer_extensions[i]);
-    }
-
-    const std::array<const char*, 1> kDesiredExtensions = {
-        {VK_KHR_SWAPCHAIN_EXTENSION_NAME},
-    };
-    const char* extensions[kDesiredExtensions.size()];
-    uint32_t num_extensions = 0;
-    for (const auto& desired_ext : kDesiredExtensions) {
-        bool available = HasExtension(info.extensions, desired_ext);
-        if (options.validate) {
-            for (size_t i = 0; !available && i < info.layer_extensions.size();
-                 i++)
-                available = HasExtension(info.layer_extensions[i], desired_ext);
-        }
-        if (available)
-            extensions[num_extensions++] = desired_ext;
-    }
-
-    VkDevice device;
-    float queue_priorities[] = {0.0};
-    const VkDeviceQueueCreateInfo queue_create_info = {
-        .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
-        .queueFamilyIndex = 0,
-        .queueCount = 1,
-        .pQueuePriorities = queue_priorities
-    };
-    // clang-format off
-    const char *kValidationLayers[] = {
-        "VK_LAYER_GOOGLE_threading",
-        "VK_LAYER_LUNARG_parameter_validation",
-        "VK_LAYER_LUNARG_device_limits",
-        "VK_LAYER_LUNARG_object_tracker",
-        "VK_LAYER_LUNARG_image",
-        "VK_LAYER_LUNARG_core_validation",
-        "VK_LAYER_LUNARG_swapchain",
-        "VK_LAYER_GOOGLE_unique_objects"
-    };
-    // clang-format on
-    uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*);
-    const VkDeviceCreateInfo create_info = {
-        .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
-        .queueCreateInfoCount = 1,
-        .pQueueCreateInfos = &queue_create_info,
-        .enabledLayerCount = (options.validate) ? num_layers : 0,
-        .ppEnabledLayerNames = kValidationLayers,
-        .enabledExtensionCount = num_extensions,
-        .ppEnabledExtensionNames = extensions,
-        .pEnabledFeatures = &info.features,
-    };
-    result = vkCreateDevice(gpu, &create_info, nullptr, &device);
-    if (result != VK_SUCCESS)
-        die("vkCreateDevice", result);
-    vkDestroyDevice(device, nullptr);
-}
-
-void GatherInfo(VulkanInfo* info, const Options& options) {
-    VkResult result;
-    uint32_t count;
-
-    result = vkEnumerateInstanceLayerProperties(&count, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceLayerProperties (count)", result);
-    do {
-        info->layers.resize(count);
-        result =
-            vkEnumerateInstanceLayerProperties(&count, info->layers.data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumerateInstanceLayerProperties (data)", result);
-    info->layer_extensions.resize(info->layers.size());
-
-    EnumerateInstanceExtensions(nullptr, &info->extensions);
-    for (size_t i = 0; i < info->layers.size(); i++) {
-        EnumerateInstanceExtensions(info->layers[i].layerName,
-                                    &info->layer_extensions[i]);
-    }
-
-    const char* kDesiredExtensions[] = {
-        VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
-    };
-    const char*
-        extensions[sizeof(kDesiredExtensions) / sizeof(kDesiredExtensions[0])];
-    uint32_t num_extensions = 0;
-    for (const auto& desired_ext : kDesiredExtensions) {
-        bool available = HasExtension(info->extensions, desired_ext);
-        if (options.validate) {
-            for (size_t i = 0; !available && i < info->layer_extensions.size();
-                 i++)
-                available =
-                    HasExtension(info->layer_extensions[i], desired_ext);
-        }
-        if (available)
-            extensions[num_extensions++] = desired_ext;
-    }
-
-    // clang-format off
-    const char *kValidationLayers[] = {
-        "VK_LAYER_GOOGLE_threading",
-        "VK_LAYER_LUNARG_parameter_validation",
-        "VK_LAYER_LUNARG_device_limits",
-        "VK_LAYER_LUNARG_object_tracker",
-        "VK_LAYER_LUNARG_image",
-        "VK_LAYER_LUNARG_core_validation",
-        "VK_LAYER_LUNARG_swapchain",
-        "VK_LAYER_GOOGLE_unique_objects"
-    };
-    // clang-format on
-    uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*);
-
-    const VkApplicationInfo application_info = {
-        .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
-        .pApplicationName = "vkinfo",
-        .applicationVersion = 0,
-        .pEngineName = "vkinfo",
-        .engineVersion = 0,
-        .apiVersion = VK_API_VERSION_1_0,
-    };
-    const VkInstanceCreateInfo create_info = {
-        .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
-        .pApplicationInfo = &application_info,
-        .enabledLayerCount = (options.validate) ? num_layers : 0,
-        .ppEnabledLayerNames = kValidationLayers,
-        .enabledExtensionCount = num_extensions,
-        .ppEnabledExtensionNames = extensions,
-    };
-    VkInstance instance;
-    result = vkCreateInstance(&create_info, nullptr, &instance);
-    if (result != VK_SUCCESS)
-        die("vkCreateInstance", result);
-
-    uint32_t num_gpus;
-    result = vkEnumeratePhysicalDevices(instance, &num_gpus, nullptr);
-    if (result != VK_SUCCESS)
-        die("vkEnumeratePhysicalDevices (count)", result);
-    std::vector<VkPhysicalDevice> gpus(num_gpus, VK_NULL_HANDLE);
-    do {
-        gpus.resize(num_gpus, VK_NULL_HANDLE);
-        result = vkEnumeratePhysicalDevices(instance, &num_gpus, gpus.data());
-    } while (result == VK_INCOMPLETE);
-    if (result != VK_SUCCESS)
-        die("vkEnumeratePhysicalDevices (data)", result);
-
-    info->gpus.resize(num_gpus);
-    for (size_t i = 0; i < gpus.size(); i++)
-        GatherGpuInfo(gpus[i], options, info->gpus.at(i));
-
-    vkDestroyInstance(instance, nullptr);
-}
-
-// ----------------------------------------------------------------------------
-
-const size_t kMaxIndent = 8;
-const size_t kIndentSize = 3;
-std::array<char, kMaxIndent * kIndentSize + 1> kIndent;
-const char* Indent(size_t n) {
-    static bool initialized = false;
-    if (!initialized) {
-        kIndent.fill(' ');
-        kIndent.back() = '\0';
-        initialized = true;
-    }
-    return kIndent.data() +
-           (kIndent.size() - (kIndentSize * std::min(n, kMaxIndent) + 1));
-}
-
-const char* VkPhysicalDeviceTypeStr(VkPhysicalDeviceType type) {
-    switch (type) {
-        case VK_PHYSICAL_DEVICE_TYPE_OTHER:
-            return "OTHER";
-        case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
-            return "INTEGRATED_GPU";
-        case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
-            return "DISCRETE_GPU";
-        case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
-            return "VIRTUAL_GPU";
-        case VK_PHYSICAL_DEVICE_TYPE_CPU:
-            return "CPU";
-        default:
-            return "<UNKNOWN>";
-    }
-}
-
-void PrintExtensions(const std::vector<VkExtensionProperties>& extensions,
-                     const Options& /*options*/,
-                     size_t indent) {
-    for (const auto& e : extensions)
-        printf("%s%s (v%u)\n", Indent(indent), e.extensionName, e.specVersion);
-}
-
-void PrintLayers(
-    const std::vector<VkLayerProperties>& layers,
-    const std::vector<std::vector<VkExtensionProperties>> extensions,
-    const Options& options,
-    size_t indent) {
-    for (size_t i = 0; i < layers.size(); i++) {
-        printf("%s%s %u.%u.%u/%u\n", Indent(indent), layers[i].layerName,
-               VK_VERSION_MAJOR(layers[i].specVersion),
-               VK_VERSION_MINOR(layers[i].specVersion),
-               VK_VERSION_PATCH(layers[i].specVersion),
-               layers[i].implementationVersion);
-        if (options.layer_description)
-            printf("%s%s\n", Indent(indent + 1), layers[i].description);
-        if (options.layer_extensions && !extensions[i].empty()) {
-            if (!extensions[i].empty()) {
-                printf("%sExtensions [%zu]:\n", Indent(indent + 1),
-                       extensions[i].size());
-                PrintExtensions(extensions[i], options, indent + 2);
-            }
-        }
-    }
-}
-
-void PrintAllFeatures(const char* indent,
-                      const VkPhysicalDeviceFeatures& features) {
-    // clang-format off
-    printf("%srobustBufferAccess: %s\n", indent, features.robustBufferAccess ? "YES" : "NO");
-    printf("%sfullDrawIndexUint32: %s\n", indent, features.fullDrawIndexUint32 ? "YES" : "NO");
-    printf("%simageCubeArray: %s\n", indent, features.imageCubeArray ? "YES" : "NO");
-    printf("%sindependentBlend: %s\n", indent, features.independentBlend ? "YES" : "NO");
-    printf("%sgeometryShader: %s\n", indent, features.geometryShader ? "YES" : "NO");
-    printf("%stessellationShader: %s\n", indent, features.tessellationShader ? "YES" : "NO");
-    printf("%ssampleRateShading: %s\n", indent, features.sampleRateShading ? "YES" : "NO");
-    printf("%sdualSrcBlend: %s\n", indent, features.dualSrcBlend ? "YES" : "NO");
-    printf("%slogicOp: %s\n", indent, features.logicOp ? "YES" : "NO");
-    printf("%smultiDrawIndirect: %s\n", indent, features.multiDrawIndirect ? "YES" : "NO");
-    printf("%sdrawIndirectFirstInstance: %s\n", indent, features.drawIndirectFirstInstance ? "YES" : "NO");
-    printf("%sdepthClamp: %s\n", indent, features.depthClamp ? "YES" : "NO");
-    printf("%sdepthBiasClamp: %s\n", indent, features.depthBiasClamp ? "YES" : "NO");
-    printf("%sfillModeNonSolid: %s\n", indent, features.fillModeNonSolid ? "YES" : "NO");
-    printf("%sdepthBounds: %s\n", indent, features.depthBounds ? "YES" : "NO");
-    printf("%swideLines: %s\n", indent, features.wideLines ? "YES" : "NO");
-    printf("%slargePoints: %s\n", indent, features.largePoints ? "YES" : "NO");
-    printf("%salphaToOne: %s\n", indent, features.alphaToOne ? "YES" : "NO");
-    printf("%smultiViewport: %s\n", indent, features.multiViewport ? "YES" : "NO");
-    printf("%ssamplerAnisotropy: %s\n", indent, features.samplerAnisotropy ? "YES" : "NO");
-    printf("%stextureCompressionETC2: %s\n", indent, features.textureCompressionETC2 ? "YES" : "NO");
-    printf("%stextureCompressionASTC_LDR: %s\n", indent, features.textureCompressionASTC_LDR ? "YES" : "NO");
-    printf("%stextureCompressionBC: %s\n", indent, features.textureCompressionBC ? "YES" : "NO");
-    printf("%socclusionQueryPrecise: %s\n", indent, features.occlusionQueryPrecise ? "YES" : "NO");
-    printf("%spipelineStatisticsQuery: %s\n", indent, features.pipelineStatisticsQuery ? "YES" : "NO");
-    printf("%svertexPipelineStoresAndAtomics: %s\n", indent, features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
-    printf("%sfragmentStoresAndAtomics: %s\n", indent, features.fragmentStoresAndAtomics ? "YES" : "NO");
-    printf("%sshaderTessellationAndGeometryPointSize: %s\n", indent, features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
-    printf("%sshaderImageGatherExtended: %s\n", indent, features.shaderImageGatherExtended ? "YES" : "NO");
-    printf("%sshaderStorageImageExtendedFormats: %s\n", indent, features.shaderStorageImageExtendedFormats ? "YES" : "NO");
-    printf("%sshaderStorageImageMultisample: %s\n", indent, features.shaderStorageImageMultisample ? "YES" : "NO");
-    printf("%sshaderStorageImageReadWithoutFormat: %s\n", indent, features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
-    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", indent, features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
-    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", indent, features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", indent, features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", indent, features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", indent, features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderClipDistance: %s\n", indent, features.shaderClipDistance ? "YES" : "NO");
-    printf("%sshaderCullDistance: %s\n", indent, features.shaderCullDistance ? "YES" : "NO");
-    printf("%sshaderFloat64: %s\n", indent, features.shaderFloat64 ? "YES" : "NO");
-    printf("%sshaderInt64: %s\n", indent, features.shaderInt64 ? "YES" : "NO");
-    printf("%sshaderInt16: %s\n", indent, features.shaderInt16 ? "YES" : "NO");
-    printf("%sshaderResourceResidency: %s\n", indent, features.shaderResourceResidency ? "YES" : "NO");
-    printf("%sshaderResourceMinLod: %s\n", indent, features.shaderResourceMinLod ? "YES" : "NO");
-    printf("%ssparseBinding: %s\n", indent, features.sparseBinding ? "YES" : "NO");
-    printf("%ssparseResidencyBuffer: %s\n", indent, features.sparseResidencyBuffer ? "YES" : "NO");
-    printf("%ssparseResidencyImage2D: %s\n", indent, features.sparseResidencyImage2D ? "YES" : "NO");
-    printf("%ssparseResidencyImage3D: %s\n", indent, features.sparseResidencyImage3D ? "YES" : "NO");
-    printf("%ssparseResidency2Samples: %s\n", indent, features.sparseResidency2Samples ? "YES" : "NO");
-    printf("%ssparseResidency4Samples: %s\n", indent, features.sparseResidency4Samples ? "YES" : "NO");
-    printf("%ssparseResidency8Samples: %s\n", indent, features.sparseResidency8Samples ? "YES" : "NO");
-    printf("%ssparseResidency16Samples: %s\n", indent, features.sparseResidency16Samples ? "YES" : "NO");
-    printf("%ssparseResidencyAliased: %s\n", indent, features.sparseResidencyAliased ? "YES" : "NO");
-    printf("%svariableMultisampleRate: %s\n", indent, features.variableMultisampleRate ? "YES" : "NO");
-    printf("%sinheritedQueries: %s\n", indent, features.inheritedQueries ? "YES" : "NO");
-    // clang-format on
-}
-
-void PrintSupportedFeatures(const char* indent,
-                            const VkPhysicalDeviceFeatures& features) {
-    // clang-format off
-    if (features.robustBufferAccess) printf("%srobustBufferAccess\n", indent);
-    if (features.fullDrawIndexUint32) printf("%sfullDrawIndexUint32\n", indent);
-    if (features.imageCubeArray) printf("%simageCubeArray\n", indent);
-    if (features.independentBlend) printf("%sindependentBlend\n", indent);
-    if (features.geometryShader) printf("%sgeometryShader\n", indent);
-    if (features.tessellationShader) printf("%stessellationShader\n", indent);
-    if (features.sampleRateShading) printf("%ssampleRateShading\n", indent);
-    if (features.dualSrcBlend) printf("%sdualSrcBlend\n", indent);
-    if (features.logicOp) printf("%slogicOp\n", indent);
-    if (features.multiDrawIndirect) printf("%smultiDrawIndirect\n", indent);
-    if (features.drawIndirectFirstInstance) printf("%sdrawIndirectFirstInstance\n", indent);
-    if (features.depthClamp) printf("%sdepthClamp\n", indent);
-    if (features.depthBiasClamp) printf("%sdepthBiasClamp\n", indent);
-    if (features.fillModeNonSolid) printf("%sfillModeNonSolid\n", indent);
-    if (features.depthBounds) printf("%sdepthBounds\n", indent);
-    if (features.wideLines) printf("%swideLines\n", indent);
-    if (features.largePoints) printf("%slargePoints\n", indent);
-    if (features.alphaToOne) printf("%salphaToOne\n", indent);
-    if (features.multiViewport) printf("%smultiViewport\n", indent);
-    if (features.samplerAnisotropy) printf("%ssamplerAnisotropy\n", indent);
-    if (features.textureCompressionETC2) printf("%stextureCompressionETC2\n", indent);
-    if (features.textureCompressionASTC_LDR) printf("%stextureCompressionASTC_LDR\n", indent);
-    if (features.textureCompressionBC) printf("%stextureCompressionBC\n", indent);
-    if (features.occlusionQueryPrecise) printf("%socclusionQueryPrecise\n", indent);
-    if (features.pipelineStatisticsQuery) printf("%spipelineStatisticsQuery\n", indent);
-    if (features.vertexPipelineStoresAndAtomics) printf("%svertexPipelineStoresAndAtomics\n", indent);
-    if (features.fragmentStoresAndAtomics) printf("%sfragmentStoresAndAtomics\n", indent);
-    if (features.shaderTessellationAndGeometryPointSize) printf("%sshaderTessellationAndGeometryPointSize\n", indent);
-    if (features.shaderImageGatherExtended) printf("%sshaderImageGatherExtended\n", indent);
-    if (features.shaderStorageImageExtendedFormats) printf("%sshaderStorageImageExtendedFormats\n", indent);
-    if (features.shaderStorageImageMultisample) printf("%sshaderStorageImageMultisample\n", indent);
-    if (features.shaderStorageImageReadWithoutFormat) printf("%sshaderStorageImageReadWithoutFormat\n", indent);
-    if (features.shaderStorageImageWriteWithoutFormat) printf("%sshaderStorageImageWriteWithoutFormat\n", indent);
-    if (features.shaderUniformBufferArrayDynamicIndexing) printf("%sshaderUniformBufferArrayDynamicIndexing\n", indent);
-    if (features.shaderSampledImageArrayDynamicIndexing) printf("%sshaderSampledImageArrayDynamicIndexing\n", indent);
-    if (features.shaderStorageBufferArrayDynamicIndexing) printf("%sshaderStorageBufferArrayDynamicIndexing\n", indent);
-    if (features.shaderStorageImageArrayDynamicIndexing) printf("%sshaderStorageImageArrayDynamicIndexing\n", indent);
-    if (features.shaderClipDistance) printf("%sshaderClipDistance\n", indent);
-    if (features.shaderCullDistance) printf("%sshaderCullDistance\n", indent);
-    if (features.shaderFloat64) printf("%sshaderFloat64\n", indent);
-    if (features.shaderInt64) printf("%sshaderInt64\n", indent);
-    if (features.shaderInt16) printf("%sshaderInt16\n", indent);
-    if (features.shaderResourceResidency) printf("%sshaderResourceResidency\n", indent);
-    if (features.shaderResourceMinLod) printf("%sshaderResourceMinLod\n", indent);
-    if (features.sparseBinding) printf("%ssparseBinding\n", indent);
-    if (features.sparseResidencyBuffer) printf("%ssparseResidencyBuffer\n", indent);
-    if (features.sparseResidencyImage2D) printf("%ssparseResidencyImage2D\n", indent);
-    if (features.sparseResidencyImage3D) printf("%ssparseResidencyImage3D\n", indent);
-    if (features.sparseResidency2Samples) printf("%ssparseResidency2Samples\n", indent);
-    if (features.sparseResidency4Samples) printf("%ssparseResidency4Samples\n", indent);
-    if (features.sparseResidency8Samples) printf("%ssparseResidency8Samples\n", indent);
-    if (features.sparseResidency16Samples) printf("%ssparseResidency16Samples\n", indent);
-    if (features.sparseResidencyAliased) printf("%ssparseResidencyAliased\n", indent);
-    if (features.variableMultisampleRate) printf("%svariableMultisampleRate\n", indent);
-    if (features.inheritedQueries) printf("%sinheritedQueries\n", indent);
-    // clang-format on
-}
-
-void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) {
-    VkResult result;
-    std::ostringstream strbuf;
-
-    printf("%s\"%s\" (%s) %u.%u.%u/%#x [%04x:%04x]\n", Indent(indent),
-           info.properties.deviceName,
-           VkPhysicalDeviceTypeStr(info.properties.deviceType),
-           VK_VERSION_MAJOR(info.properties.apiVersion),
-           VK_VERSION_MINOR(info.properties.apiVersion),
-           VK_VERSION_PATCH(info.properties.apiVersion),
-           info.properties.driverVersion, info.properties.vendorID,
-           info.properties.deviceID);
-
-    for (uint32_t heap = 0; heap < info.memory.memoryHeapCount; heap++) {
-        if ((info.memory.memoryHeaps[heap].flags &
-             VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
-            strbuf << "DEVICE_LOCAL";
-        printf("%sHeap %u: %" PRIu64 " MiB (0x%" PRIx64 " B) %s\n",
-               Indent(indent + 1), heap,
-               info.memory.memoryHeaps[heap].size / 0x100000,
-               info.memory.memoryHeaps[heap].size, strbuf.str().c_str());
-        strbuf.str(std::string());
-
-        for (uint32_t type = 0; type < info.memory.memoryTypeCount; type++) {
-            if (info.memory.memoryTypes[type].heapIndex != heap)
-                continue;
-            VkMemoryPropertyFlags flags =
-                info.memory.memoryTypes[type].propertyFlags;
-            if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
-                strbuf << " DEVICE_LOCAL";
-            if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
-                strbuf << " HOST_VISIBLE";
-            if ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
-                strbuf << " COHERENT";
-            if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
-                strbuf << " CACHED";
-            if ((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
-                strbuf << " LAZILY_ALLOCATED";
-            printf("%sType %u:%s\n", Indent(indent + 2), type,
-                   strbuf.str().c_str());
-            strbuf.str(std::string());
-        }
-    }
-
-    for (uint32_t family = 0; family < info.queue_families.size(); family++) {
-        const VkQueueFamilyProperties& qprops = info.queue_families[family];
-        VkQueueFlags flags = qprops.queueFlags;
-        char flags_str[5];
-        flags_str[0] = (flags & VK_QUEUE_GRAPHICS_BIT) ? 'G' : '_';
-        flags_str[1] = (flags & VK_QUEUE_COMPUTE_BIT) ? 'C' : '_';
-        flags_str[2] = (flags & VK_QUEUE_TRANSFER_BIT) ? 'T' : '_';
-        flags_str[3] = (flags & VK_QUEUE_SPARSE_BINDING_BIT) ? 'S' : '_';
-        flags_str[4] = '\0';
-        printf(
-            "%sQueue Family %u: %ux %s\n"
-            "%stimestampValidBits: %ub\n"
-            "%sminImageTransferGranularity: (%u,%u,%u)\n",
-            Indent(indent + 1), family, qprops.queueCount, flags_str,
-            Indent(indent + 2), qprops.timestampValidBits, Indent(indent + 2),
-            qprops.minImageTransferGranularity.width,
-            qprops.minImageTransferGranularity.height,
-            qprops.minImageTransferGranularity.depth);
-    }
-
-    printf("%sFeatures:\n", Indent(indent + 1));
-    if (options.unsupported_features) {
-        PrintAllFeatures(Indent(indent + 2), info.features);
-    } else {
-        PrintSupportedFeatures(Indent(indent + 2), info.features);
-    }
-
-    printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size());
-    if (!info.extensions.empty())
-        PrintExtensions(info.extensions, options, indent + 2);
-    printf("%sLayers [%zu]:\n", Indent(indent + 1), info.layers.size());
-    if (!info.layers.empty())
-        PrintLayers(info.layers, info.layer_extensions, options, indent + 2);
-}
-
-void PrintInfo(const VulkanInfo& info, const Options& options) {
-    std::ostringstream strbuf;
-    size_t indent = 0;
-
-    printf("%sInstance Extensions [%zu]:\n", Indent(indent),
-           info.extensions.size());
-    PrintExtensions(info.extensions, options, indent + 1);
-    printf("%sInstance Layers [%zu]:\n", Indent(indent), info.layers.size());
-    if (!info.layers.empty())
-        PrintLayers(info.layers, info.layer_extensions, options, indent + 1);
-
-    printf("%sPhysicalDevices [%zu]:\n", Indent(indent), info.gpus.size());
-    for (const auto& gpu : info.gpus)
-        PrintGpuInfo(gpu, options, indent + 1);
-}
-
-const char kUsageString[] =
-    "usage: vkinfo [options]\n"
-    "  -v                       enable all the following verbose options\n"
-    "    -layer_description     print layer description strings\n"
-    "    -layer_extensions      print extensions supported by each layer\n"
-    "    -unsupported_features  print all physical device features\n"
-    "  -validate                enable validation layers if present\n"
-    "  -debug_pause             pause at start until resumed via debugger\n";
-
-}  // namespace
-
-// ----------------------------------------------------------------------------
-
-int main(int argc, char const* argv[]) {
-    static volatile bool startup_pause = false;
-    Options options = {
-        .layer_description = false, .layer_extensions = false,
-        .unsupported_features = false,
-        .validate = false,
-    };
-    for (int argi = 1; argi < argc; argi++) {
-        if (strcmp(argv[argi], "-h") == 0) {
-            fputs(kUsageString, stdout);
-            return 0;
-        }
-        if (strcmp(argv[argi], "-v") == 0) {
-            options.layer_description = true;
-            options.layer_extensions = true;
-            options.unsupported_features = true;
-        } else if (strcmp(argv[argi], "-layer_description") == 0) {
-            options.layer_description = true;
-        } else if (strcmp(argv[argi], "-layer_extensions") == 0) {
-            options.layer_extensions = true;
-        } else if (strcmp(argv[argi], "-unsupported_features") == 0) {
-            options.unsupported_features = true;
-        } else if (strcmp(argv[argi], "-validate") == 0) {
-            options.validate = true;
-        } else if (strcmp(argv[argi], "-debug_pause") == 0) {
-            startup_pause = true;
-        }
-    }
-
-    while (startup_pause) {
-        sleep(0);
-    }
-
-    VulkanInfo info;
-    GatherInfo(&info, options);
-    PrintInfo(info, options);
-    return 0;
-}
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index a626e48..8528898 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library_shared {
     name: "libvkjson",
     srcs: [
         "vkjson.cc",
@@ -15,11 +15,14 @@
     export_include_dirs: [
         ".",
     ],
+    shared_libs: [
+        "libvulkan",
+    ],
     whole_static_libs: [
         "libjsoncpp",
     ],
-    header_libs: [
-        "vulkan_headers",
+    export_shared_lib_headers: [
+        "libvulkan",
     ],
 }
 
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 3da4336..8f714d8 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -21,11 +21,14 @@
 #include "vkjson.h"
 
 #include <assert.h>
-#include <string.h>
 #include <stdlib.h>
+#include <string.h>
 
-#include <cmath>
+#include <json/json.h>
+
+#include <algorithm>
 #include <cinttypes>
+#include <cmath>
 #include <cstdio>
 #include <limits>
 #include <memory>
@@ -33,8 +36,6 @@
 #include <type_traits>
 #include <utility>
 
-#include <json/json.h>
-
 namespace {
 
 inline bool IsIntegral(double value) {
@@ -46,6 +47,14 @@
 #endif
 }
 
+// Floating point fields of Vulkan structure use single precision. The string
+// output of max double value in c++ will be larger than Java double's infinity
+// value. Below fake double max/min values are only to serve the safe json text
+// parsing in between C++ and Java, becasue Java json library simply cannot
+// handle infinity.
+static const double SAFE_DOUBLE_MAX = 0.99 * std::numeric_limits<double>::max();
+static const double SAFE_DOUBLE_MIN = -SAFE_DOUBLE_MAX;
+
 template <typename T> struct EnumTraits;
 template <> struct EnumTraits<VkPhysicalDeviceType> {
   static uint32_t min() { return VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE; }
@@ -851,7 +860,8 @@
 
 template <typename T, typename = EnableForArithmetic<T>>
 inline Json::Value ToJsonValue(const T& value) {
-  return Json::Value(static_cast<double>(value));
+  return Json::Value(
+      std::clamp(static_cast<double>(value), SAFE_DOUBLE_MIN, SAFE_DOUBLE_MAX));
 }
 
 inline Json::Value ToJsonValue(const uint64_t& value) {